• Java基础27,28(多线程,ThreadMethod ,线程安全问题,线程状态,线程池)


    目录

    一、多线程

    1. 概述

    2. 进程与线程

    2.1 程序

    2.2 进程

    2.3 线程

    2.4 进程与线程的区别

    3. 线程基本概念

    4.并发与并行

    5. 线程的创建方式

    方式一:继承Thread类

     方式二:实现Runable接口

    方式三:实现Callable接口

    方式四:线程池

    小结:

    二、ThreadMethod 

    1. Thread类常用的构造方法

    2. 线程的命名 

    3. 获取当前线程

    4. 线程的休眠(暂停)

    5. 线程的优先级

    6. 守护线程(Daemon Thread)

    用户线程与守护线程的区别

    设置守护线程

    7. 线程的让出:yield( )方法

    8. 线程的插队:join( )方法 

    9. 小结

    三、线程安全问题

    多线程售票问题

    解决方案1

    解决方案2

    解决方案3

    四、线程状态

    1. New:新建状态

    2. Runnable:运行状态

    3. Terminated:终止状态

    4. Blocked:阻塞状态

    5. Timed Waiting:计时等待状态

    6.  Waiting:等待状态

    五、线程池

    1. 什么是线程池?

    2. 线程池常用类和接口

    3. 线程池常见方法

    执行线程任务

    4. 线程池分类总结

    FixedThreadPool

    CachedThreadPool

    SingleThreadExecutor

    ScheduledThreadPool

    5. 线程池的配置参数

    6. 线程池的执行流程

    7. 线程池的状态


    一、多线程

    1. 概述

    现代操作系统(Windows,macOS,Linux)都可以执行多任务。多任务就是同时运行多个任务。例如:播放音乐的同时,浏览器可以进行文件下载,同时可以进行QQ消息的收发。
    CPU执行代码都是一条一条顺序执行的,但是,即使是单核CPU,也可以同时运行多个任务。因为操作系统执行多任务实际上就是让CPU对多个任务轮流交替执行。
    操作系统轮流让多个任务交替执行,例如,让浏览器执行0.001秒,让QQ执行0.001秒,再让音乐播放器执行0.001秒。在用户使用的体验看来,CPU就是在同时执行多个任务。

    2. 进程与线程

    2.1 程序

    程序是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,可以理解为程序是包含静态代码的文件。例如:浏览器软件、音乐播放器软件等软件的安装目录和文件。

    2.2 进程

    进程是程序的一次执行过程,是系统运行程序的基本单位。在Windows系统中,每一个正在执行的exe文件或后台服务,都是一个进程,由操作系统统一管理并分配资源,因此进程是动态的。 例如:正在运行中的浏览器就是一个进程,正在运行中的音乐播放器是另一个进程,同理,正在运行中的QQ和WPS等都是进程。
    操作系统运行一个程序,即是一个进程从创建,运行到消亡的过程。简单来说,一个进程就是一个执行中的程序,它在计算机中一个指令接着一个指令地执行着, 同时,每个进程还占有某些系统资源如 CPU时间,内存空间,文件,输入输 出设备的使用权等。

    2.3 线程

    某些进程内部还需要同时执行多个子任务。例如,我们在使用WPS时,WPS可以让我们一边打字,一边进行拼写检查,同时还可以在后台进行自动保存和上传云文档,我们把子任务称为线程。线程是进程划分成的更小的运行单位。
    进程和线程的关系就是:一个进程可以包含一个或多个线程,但至少会有一个主线程。

    2.4 进程与线程的区别

    • 根本区别:进程是操作系统资源分配的基本单位,而线程是处理器任务调度和执行的基本单位;
    • 资源开销:每个进程都有独立的代码副本和数据空间,进程之间的切换,资源开销较大;线程可以看做轻量级的进程,每个线程都有自己独立的运行栈和程序计数器,线程之间切换,资源开销小;
    • 包含关系:一个进程内包含有多个线程,在执行过程,线程的执行不是线性串行的,而是多条线程并行共同完成;
    • 内存分配:同一进程内的所有线程共享本进程的内存空间和资源;进程之间的内存空间和资源相互独立;
    • 影响关系:一个进程崩溃后,在保护模式下不会对其他进程产生影响;一个线程崩溃,会导致整个进程退出。所以多进程要比多线程健壮;
    • 执行过程:每个独立的进程有程序运行的入口和程序出口。但是线程不能独立执行,必须依存在应用程序(进程)中,由应用程序提供多个线程执行控制;

    3. 线程基本概念

    单线程:单线程就是进程中只有一个线程。单线程在程序执行时,所走的程序路径按照连续顺序排下来,前面的必须处理好,后面的才会执行。

    1. public static void main(String[] args) {
    2. int a =10;
    3. int b =20;
    4. int c=a+b;
    5. System.out.println(c);
    6. }

    多线程:由一个以上的线程组成的程序称为多线程程序。Java中,一定是从主线程开始执行(main方法),然后在主线程的某个位置创建并启动新的线程。

    作用:最主要是为了提高程序的运行效率
    多线程应用场景:软件中耗时的操作  拷贝,迁移文件,加载大量的时候


    为什么要有多线程?
    线程:操作系统能够进行调度的最小单元,他被包含在进程中,是进程中实际运行的单位
    进程:进程是程序的基本执行实体

    4.并发与并行

    并发:在同一时刻,有多个指令在单个Cpu上交替执行
    并行:在同一时刻,有多个指令在单个Cpu上同时执行

    5. 线程的创建方式

    方式一:继承Thread类

    继承 java.lang.Thread 类(线程子类)

    创建多线程的方法1:

    1. 自定义一个类继承Thread
    2. 重写run方法
    3. 创建子类的对象,并启动线程

     MyThread类:

    1. public class MyThread extends Thread {
    2. public MyThread() {
    3. super();
    4. }
    5. @Override
    6. public void run() {
    7. //写线程要执行的代码
    8. for (int i = 0; i < 100; i++) {
    9. System.out.println(getName()+"_hello");
    10. }
    11. }
    12. }

    Demo01类:

    1. public class Demo01 {
    2. public static void main(String[] args) {
    3. /**
    4. *创建多线程的方法1:
    5. *1.自定义一个类继承Thread
    6. *2.重写run方法
    7. *3.创建子类的对象,并启动线程
    8. */
    9. MyThread t1 = new MyThread();
    10. t1.setName("线程1");//给线程设置名称
    11. t1.start();//此处需要注意,不能直接调用run方法,否则就是对象的创建和方法的调用
    12. MyThread t2 = new MyThread();
    13. t2.setName("线程2");
    14. t2.start();
    15. for (int i = 0; i < 15; i++) {
    16. System.out.println(Thread.currentThread().getName()+"_world");
    17. }
    18. }
    19. }

     方式二:实现Runable接口

    实现 java.lang.Runnable 接口(线程执行类)

    创建多线程的方法2:

    1. 自己定义一个类实现Runable接口
    2. 重写里面的run方法
    3. 创建自己的类的对象
    4. 创建Thread类的对象,并开启线程

    MyRun类:

    1. public class MyRun implements Runnable {
    2. @Override
    3. public void run() {
    4. // 写线程要执行的代码
    5. for (int i = 0; i < 100; i++) {
    6. System.out.println(Thread.currentThread().getName() + "_hello");
    7. }
    8. }
    9. }

    Demo02类:

    1. public class Demo02 {
    2. public static void main(String[] args) {
    3. /**
    4. * 1.自己定义一个类实现Runable接口 2.重写里面的run方法 3.创建自己的类的对象 4.创建Thread类的对象,并开启线程
    5. */
    6. // 创建MyRun多线程,标识多线程要执行的任务
    7. MyRun run = new MyRun();
    8. // 创建线程
    9. Thread t1 = new Thread(run);
    10. Thread t2 = new Thread(run);
    11. t1.setName("线程1");
    12. t2.setName("线程2");
    13. t1.start();
    14. t2.start();
    15. }
    16. }

    方式三:实现Callable接口

    实现 java.util.concurrent.Callable 接口,允许子线程返回结果、抛出异常

    创建多线程的方法3:

    1. 创建Callable是实现类
    2. 重写ca11方法 
    3. 创建Mycal1实现类的对象 
    4. 创建FutureTask的对象(管理多线程运行的结果)
    5. 创建Thread对象,并启动

    MyCall类:

    1. public class MyCall implements Callable{
    2. //求1-100之间的和
    3. @Override
    4. public Integer call() throws Exception {
    5. int sum = 0;
    6. for (int i = 1; i <= 100; i++) {
    7. sum+=i;
    8. }
    9. return sum;
    10. }
    11. }

    Demo03类:

    1. public class Demo03 {
    2. public static void main(String[] args) throws InterruptedException, ExecutionException {
    3. /**
    4. * 1.创建Callable是实现类
    5. * 2.重写ca11方法
    6. * 3.创建Mycal1实现类的对象
    7. * 4.创建FutureTask的对象(管理多线程运行的结果)
    8. * 5.创建Thread对象,并启动
    9. */
    10. MyCall call1 = new MyCall();
    11. FutureTask ft = new FutureTask(call1);
    12. Thread t1 = new Thread(ft);
    13. t1.start();
    14. //获取多线程运行后的结果
    15. Integer result = ft.get();
    16. System.out.println(result);

    方式四:线程池

    1. //通过线程池创建线程
    2. //ThreadPoolExecutor类:线程池
    3. // Executors类:工具类,用于创建各种参数配合的线程池
    4. // ExecutorService接口:定义线程池的行为
    5. public static void main(String[] args) {
    6. //创建线程池
    7. ExecutorService executorService = Executors.newFixedThreadPool(10);
    8. //提交“线程任务”给线程池
    9. for (int i = 0; i < 100; i++) {
    10. executorService.execute(new Runnable() {
    11. @Override
    12. public void run() {
    13. System.out.println("线程池中的" + Thread.currentThread().getName() + "开始执行。。。。");
    14. }
    15. });
    16. }
    17. //关闭线程池
    18. executorService.shutdown();

    详情见线程池(见五、线程池)

    小结:

    继承Thread类          

    优点:编程比较简单,可以直接使用Thread类中的方法,      

    缺点:可扩展性比较差,不能再继承其他的类
    实现Runable接口    

    优点:扩展性强,实现该接口后还可以同时继承其他类,实现其他的接口    

    缺点:编程相对比较复杂,不能直接使用Thread类中的方法
    实现Callable接口    

    优点:有返回值,可以利用FutureTask进行结果的保存          

    缺点:编程相对比较复杂,不能直接使用Thread类中的方法    

      

    注意:直接调用Thread实例的run()方法是无效的,因为直接调用run()方法,相当于调用了一个普通的Java方法,当前线程并没有任何改变,也不会启动新线程。                         

    二、ThreadMethod 

    1. Thread类常用的构造方法

    1. public class MyThread extends Thread {
    2. public MyThread() {
    3. super();
    4. }
    5. public MyThread(String name) {
    6. super(name);
    7. }
    8. @Override
    9. public void run() {
    10. for (int i = 0; i < 20; i++) {
    11. try {
    12. sleep(100);
    13. } catch (InterruptedException e) {
    14. // TODO Auto-generated catch block
    15. e.printStackTrace();
    16. }
    17. System.out.println(getName() + "@" + i);
    18. }
    19. }
    20. }
    1. Thread类常用的构造方法:
    2. Thread()
    3. Thread(String name)
    4. Thread(Runnable run,String name,
    5. Thread(Runnable run)
    1. MyThread t1 = new MyThread();
    2. // 通过构造方法创建线程
    3. MyThread t2 = new MyThread("线程2");

    2. 线程的命名 

    生产环境中,为了排查问题方便,建议在创建线程的时候指定一个合理的线程名字

    • 调用父类的setName()方法或在构造方法中给线程名字赋值;
    1. // getName():获取线程名称
    2. // 细节:如果给线程没有设置名称,线程也有默认的名称 Thread-x(从0开始)
    3. // setName()设置线程的名称构造方法也可以设置线程的名称
    4. MyThread t1 = new MyThread();
    5. t1.setName("线程1");
    • 如果没有为线程命名,系统会默认指定线程名,命名规则是Thread-N的形式

    3. 获取当前线程

    Thread.currentThread()

    1. // Thread.currentThread()获取当前线程对象
    2. Thread tmain = Thread.currentThread();
    3. tmain.setName("核心线程");
    4. for (int i = 0; i < 20; i++) {
    5. System.out.println(tmain.getName() + "@" + i);
    6. }

    4. 线程的休眠(暂停)

    在线程中,可以通过调用Thread.sleep(long millis),强迫当前线程按照指定毫秒值休眠。

    1. public class Main {
    2. public static void main(String[] args) {
    3. System.out.println("main start...");
    4. Thread t = new Thread() {
    5. public void run() {
    6. System.out.println("thread run...");
    7. try {
    8. Thread.sleep(10); // 子线程休眠10毫秒
    9. } catch (InterruptedException e) {}
    10. System.out.println("thread end.");
    11. }
    12. };
    13. t.start();
    14. try {
    15. Thread.sleep(20); // 主线程休眠20毫秒
    16. } catch (InterruptedException e) {}
    17. System.out.println("main end...");
    18. }
    19. }

    5. 线程的优先级

    • 在线程中,通过setPriority(int n)设置线程优先级,范围是1-10,默认为 5
    • 优先级高的线程被操作系统调度的优先级较高(操作系统对高优先级线程,调度更频繁)
    • 注意:并不能代表,通过设置优先级来确保高优先级的线程一定会先执行
    • 案例:
      1. public static void main(String[] args) {
      2. //创建一个匿名内部类对象run
      3. Runnable run = new Runnable() {
      4. @Override
      5. public void run() {
      6. for (int i = 0; i < 20; i++) {
      7. System.out.println(Thread.currentThread().getName() + "@" + i);
      8. }
      9. }
      10. };
      11. Thread t1 = new Thread(run, "线程1");
      12. Thread t2 = new Thread(run, "线程2");
      13. //获取线程的优先级:默认为5
      14. System.out.println(t1.getPriority());
      15. System.out.println("线程t2的优先级为" + t2.getPriority());
      16. System.out.println("主的优先级为:" + Thread.currentThread().getPriority());
      17. //设置优先级,优先级的取值范围1-10之间,优先级越高,抢到的线程概率越大,但并不是绝对的
      18. t1.setPriority(10);
      19. t2.setPriority(1);
      20. t1.start();
      21. t2.start();
      22. }

    6. 守护线程(Daemon Thread)

    用户线程与守护线程的区别

    • 用户线程:我们平常创建的普通线程;
    • 守护线程:用来服务于用户线程的线程,在JVM中,所有非守护线程都执行完毕后,无论有没有守护线程,虚拟机都会自动退出;而守护线程执行结束后,虚拟机不会自动退出。

    设置守护线程

    守护线程:备胎线程--setDaemon(true)
            细节:当其他的非守护的线程执行完毕后,守护线程会陆续结束
            即(当女神线程结束,那么备胎线程也没有存在的必要)

    在调用start()方法前,调用setDaemon(true)把该线程标记为守护线程

    1. public static void main(String[] args) {
    2. //守护线程:备胎线程--setDaemon(true)
    3. //细节:当其他的非守护的线程执行完毕后,守护线程会陆续结束
    4. //即(当女神线程结束,那么备胎线程也没有存在的必要)
    5. //创建线程1
    6. Thread t1 = new Thread() {
    7. @Override
    8. public void run() {
    9. for (int i = 1; i <=10; i++) {
    10. System.out.println(getName()+"@"+i);
    11. }
    12. };
    13. };
    14. //创建线程2
    15. Thread t2 = new Thread() {
    16. @Override
    17. public void run() {
    18. for (int i = 1; i <=100; i++) {
    19. System.out.println(getName()+"@"+i);
    20. }
    21. };
    22. };
    23. //给线程设置名称
    24. t1.setName("女神");
    25. t2.setName("备胎");
    26. //将第二个线程设置为守护线程
    27. t2.setDaemon(true);
    28. t1.start();
    29. t2.start();
    30. }

    7. 线程的让出:yield( )方法

    线程通过调用yield()方法告诉JVM的线程调度,当前线程愿意让出CPU给其他线程使用。
    ○至于系统是否采纳,取决于JVM的线程调度模型:分时调度模型和抢占式调度模型

    • 分时调度模型:所有的线程轮流获得 cpu的使用权,并且平均分配每个线程占用的 CPU 时间片;
    • 抢占式调度模型:优先让可运行池中优先级高的线程占用 CPU,如果可运行池中的线程优先级相同,那么就随机选择一个线程,使其占用CPU。(JVM虚拟机采用的是抢占式调度模型 )
    1. public class MyThread1 extends Thread {
    2. //当前线程让出执行权后,再次进行抢夺也可能会抢夺到相关资源
    3. @Override
    4. public void run() {
    5. for (int i = 1; i <= 100; i++) {
    6. System.out.println(getName() + "@" + i);
    7. //表示让出当前的cpu的执行权,会让结果更加均匀一点,但是并非绝对
    8. Thread.yield();//出让线程,礼让线程
    9. }
    10. }
    11. }
    1. public class Demo04 {
    2. public static void main(String[] args) {
    3. //创建线程1和线程2
    4. //出让线程,礼让线程Thread.yield();
    5. MyThread1 t1 = new MyThread1();
    6. MyThread1 t2 = new MyThread1();
    7. t1.setName("线程1");
    8. t2.setName("线程2");
    9. t1.start();
    10. t2.start();
    11. }
    12. }

    8. 线程的插队:join( )方法 

    t.join()方法会使当前线程( 主线程 或者调用t.join()的线程 )进入等待池,并等待 线程t 执行完毕后才会被唤醒。此时,并不影响同一时刻处在运行状态的其他线程。

    1. public class Demo05 {
    2. public static void main(String[] args) throws InterruptedException {
    3. //线程对象.join();插队线程
    4. //此时土豆线程会和main线程抢占cpu的资源
    5. //如果土豆先执行,后执行main就用到join方法
    6. MyThread2 t1 = new MyThread2();
    7. t1.setName("土豆");
    8. t1.start();
    9. //表示把t1线程插入到当前的线程前面
    10. t1.join();
    11. for (int i = 0; i <=100; i++) {
    12. System.out.println(Thread.currentThread().getName()+"@"+i);
    13. }
    14. }
    15. }
    16. class MyThread2 extends Thread {
    17. @Override
    18. public void run() {
    19. for (int i = 1; i <= 100; i++) {
    20. System.out.println(getName() + "@" + i);
    21. }
    22. }
    23. }

    9. 小结

    Thread线程的方法

    • getName()--有默认的线程名
    • setName()--给线程设置名称,构造方法也可以设置线程名
    • Thread.currentThread(),获取当前线程
    • getPriority() 获取线程的优先级1-10,默认的优先级为5
    • setPriority(int) 数字越大优先级越大
    • sleep(long mill)   睡眠
    • join()  插队线程
    • yield() 礼让线程
    • setDaemon(boolean) 守护线程

    三、线程安全问题

    多线程售票问题

    1. public class MyThread extends Thread {
    2. //表示当前的变量提升到了类级别,所有的类共享此成员变量
    3. static int ticket = 0;
    4. @Override
    5. public void run() {
    6. // 卖票
    7. while (true) {
    8. if (ticket < 10) {
    9. try {
    10. Thread.sleep(1000);
    11. } catch (InterruptedException e) {
    12. // TODO Auto-generated catch block
    13. e.printStackTrace();
    14. }
    15. ticket++;
    16. System.out.println(getName() + "正在卖出第" + ticket + "张票");
    17. } else {
    18. break;
    19. }
    20. }
    21. }
    22. }
    1. public class Demo01 {
    2. public static void main(String[] args) {
    3. //线程在进行执行的过程中,cpu的执行权随时都可能被其他线程给抢走
    4. MyThread t1 = new MyThread();
    5. MyThread t2 = new MyThread();
    6. MyThread t3 = new MyThread();
    7. t1.setName("窗口1");
    8. t2.setName("窗口2");
    9. t3.setName("窗口3");
    10. //线程启动开始卖票
    11. t1.start();
    12. t2.start();
    13. t3.start();
    14. }
    15. }

    当多个线程同时运行时,线程的调度由操作系统决定,程序本身无法决定。因此,任何一个线程都有可能在任何指令处被操作系统暂停,然后在某个时间段后继续执行。
    这个时候,一个在单线程模型下不存在的问题就会发生:如果多个线程同时读写共享变量,会出现数据不一致的问题。

    解决方案1

    解放方案1:锁代码块:

    1. synchronized(锁对象){//锁对象可以是任意类型对象,但是必须要保证多个线程使用同一个锁
    2.  操作的共享的数据资源
    3.  }

    默认下,锁是打开装填,有一个线程进来,锁自动关闭
    里面的代码执行完毕,线程出来,锁自动打开

    1. public class MyThread extends Thread {
    2. // 表示当前的变量提升到了类级别,所有的类共享此成员变量
    3. static int ticket = 0;
    4. //注意事项:锁对象要唯一
    5. static Object obj = new Object();
    6. @Override
    7. public void run() {
    8. // 卖票
    9. while (true) {
    10. synchronized (obj) {
    11. if (ticket < 1000) {
    12. try {
    13. Thread.sleep(100);
    14. } catch (InterruptedException e) {
    15. // TODO Auto-generated catch block
    16. e.printStackTrace();
    17. }
    18. ticket++;
    19. System.out.println(getName() + "正在卖出第" + ticket + "张票");
    20. } else {
    21. break;
    22. }
    23. }
    24. }
    25. }
    26. }

    解决方案2

    解放方案2:锁方法:

    1. 修饰符 synchronized 返回值类型 方法名(){
    2. 括号内写的是共享的资源
    3. }

    1.特点:同步方法锁锁住的方法中的代码
    2.特点:方法上面的锁不能自己指定,
                实例方法:通过当前对象,作为“锁”
                静态方法:通过当前类class字节码对象,作为“锁

    1. public class MyThread extends Thread {
    2. // 表示当前的变量提升到了类级别,所有的类共享此成员变量
    3. static int ticket = 0;
    4. @Override
    5. public void run() {
    6. // 卖票
    7. while (true) {
    8. if (Method()) {
    9. break;
    10. }
    11. }
    12. }
    13. public static synchronized boolean Method() {
    14. if (ticket < 100) {
    15. try {
    16. Thread.sleep(100);
    17. } catch (InterruptedException e) {
    18. // TODO Auto-generated catch block
    19. e.printStackTrace();
    20. }
    21. ticket++;
    22. System.out.println(Thread.currentThread().getName() + "正在卖出第" + ticket + "张票");
    23. } else {
    24. return true;
    25. }
    26. return false;
    27. }
    28. }

    1. public class Demo02 {
    2. public static void main(String[] args) {
    3. //MyRun作为Thread线程的参数进行传值
    4. MyRun run =new MyRun();
    5. Thread t1 = new Thread(run,"线程1");
    6. Thread t2 = new Thread(run,"线程2");
    7. Thread t3 = new Thread(run,"线程3");
    8. t1.start();
    9. t2.start();
    10. t3.start();
    11. }
    12. }
    13. class MyRun implements Runnable{
    14. static int ticket =0;
    15. @Override
    16. public void run() {
    17. while (true) {
    18. if (Method()) {
    19. break;
    20. }
    21. }
    22. }
    23. //此处锁对象为当前的对象
    24. public static synchronized boolean Method() {
    25. if (ticket < 10000) {
    26. try {
    27. Thread.sleep(100);
    28. } catch (InterruptedException e) {
    29. // TODO Auto-generated catch block
    30. e.printStackTrace();
    31. }
    32. ticket++;
    33. System.out.println(Thread.currentThread().getName() + "正在卖出第" + ticket + "张票");
    34. } else {
    35. return true;
    36. }
    37. return false;
    38. }
    39. }

    解决方案3

    解放方案3:Lock锁  1.5:

    可以手动开锁关锁
    lock();加锁
    unlock();解锁

    1. //Lock锁 1.5
    2. //可以手动开锁关锁
    3. //lock();加锁
    4. //unlock();解锁
    5. public class MyThread extends Thread {
    6. // 表示当前的变量提升到了类级别,所有的类共享此成员变量
    7. static int ticket = 0;
    8. static Lock lock = new ReentrantLock();
    9. @Override
    10. public void run() {
    11. while (true) {
    12. lock.lock();
    13. try {
    14. if (ticket < 100) {
    15. ticket++;
    16. System.out.println(getName() + "正在卖出第" + ticket + "张票");
    17. } else {
    18. break;
    19. }
    20. } catch (Exception e) {
    21. // TODO Auto-generated catch block
    22. e.printStackTrace();
    23. }finally {
    24. lock.unlock();
    25. }
    26. }
    27. }
    28. }

    四、线程状态

    在Java程序中,一个线程对象通过调用start()方法启动线程,并且在线程获取CPU时,自动执行run()方法。run()方法执行完毕,代表线程的生命周期结束。
    ●在整个线程的生命周期中,线程的状态有以下6种:

    • New:新建状态,新创建的线程,此时尚未调用start()方法;
    • Runnable:运行状态,运行中的线程,已经调用start()方法,线程正在或即将执行run()方法;
    • Blocked:阻塞状态,运行中的线程,在等待竞争锁时,被阻塞,暂不执行;
    • Waiting:等待状态,运行中的线程,因为join()等方法调用,进入等待;
    • Timed Waiting:计时等待状态,运行中的线程,因为执行sleep(等待毫秒值)join(等待毫秒值)等方法,进入计时等待;
    • Terminated:终止状态,线程已终止,因为run()方法执行完毕。

    ●当线程启动后,它可以在Runnable、Blocked、Waiting和Timed Waiting这几个状态之间切换,直到最后变成Terminated状态,线程终止

    线程终止的原因有:
    ○线程正常终止:run()方法执行到return语句返回;
    ○线程意外终止:run()方法因为未捕获的异常导致线程终止;
    ○对某个线程的Thread实例调用stop()方法强制终止(宇宙超级无敌强烈不推荐);

    1. New:新建状态

    新建状态:线程对象创建,还没有调用start方法的时候

    1. //新建状态:线程对象创建,还没有调用start方法的时候
    2. public class NewState {
    3. public static void main(String[] args) {
    4. Thread thread = new Thread() {
    5. @Override
    6. public void run() {
    7. System.out.println("正在执行某件任务");
    8. };
    9. };
    10. System.out.println(thread.getState());//NEW
    11. }
    12. }

    2. Runnable:运行状态

    线程对象调用start()方法,处于等待CPU分配资源,running ready

    1. //线程对象调用start()方法,处于等待CPU分配资源,running ready
    2. public class RunState {
    3. public static void main(String[] args) {
    4. Thread thread = new Thread() {
    5. @Override
    6. public void run() {
    7. System.out.println("正在执行某件任务");
    8. };
    9. };
    10. thread.start();
    11. System.out.println(thread.getState());// RUNNABLE
    12. }
    13. }

    3. Terminated:终止状态

    TERMINATED操作系统注销此

    1. //TERMINATED操作系统注销此
    2. public class TerminatedState {
    3. public static void main(String[] args) throws InterruptedException {
    4. Thread thread = new Thread() {
    5. @Override
    6. public void run() {
    7. System.out.println("正在执行某件任务");
    8. };
    9. };
    10. thread.start();
    11. Thread.sleep(1000);
    12. System.out.println(thread.getState());//TERMINATED
    13. }
    14. }

    4. Blocked:阻塞状态

    运行中的线程,在等待竞争锁时,被阻塞,暂不执行

    1. public class BlockedState {
    2. public static void main(String[] args) throws InterruptedException {
    3. Runnable run = new Runnable() {
    4. @Override
    5. public void run() {
    6. synchronized (this) {
    7. //死循环
    8. while (true);
    9. }
    10. }
    11. };
    12. //创建线程t1和t2,并启动线程
    13. Thread t1 = new Thread(run, "t1");
    14. Thread t2 = new Thread(run, "t2");
    15. t1.start();
    16. t2.start();
    17. //让主线程睡眠1秒,目的想让t1和t2同时竞争一个锁
    18. Thread.sleep(1000);
    19. System.out.println("t1 state" + t1.getState());
    20. System.out.println("t2 state" + t2.getState());
    21. }
    22. }

    5. Timed Waiting:计时等待状态

    运行中的线程,因为执行sleep(等待毫秒值)join(等待毫秒值)等方法,进入计时等待;

    1. // wait()
    2. // notify()
    3. // notifyA11()
    4. //注意事项:
    5. //1.要和synchronized写在一起
    6. //2.锁对象要和调用的对象保持一致
    7. //TIMED WAITING计时等待
    8. //执行sleep(1ong millis)等方法 不会释放锁资源
    9. //执行wait(long millis)等方法 会释放所资源
    10. // join(等待时间)
    11. public class TimeWaitingState {
    12. public static void main(String[] args) throws InterruptedException {
    13. Runnable run = new Runnable() {
    14. @Override
    15. public void run() {
    16. synchronized (this) {
    17. System.out.println(Thread.currentThread().getName());
    18. try {
    19. // Thread.sleep(5000);
    20. this.wait(5000);
    21. } catch (InterruptedException e) {
    22. // TODO Auto-generated catch block
    23. e.printStackTrace();
    24. }
    25. }
    26. }
    27. };
    28. Thread t1 = new Thread(run, "t1");
    29. Thread t2 = new Thread(run, "t2");
    30. // 期待线程,观察sleep方法和wait方法对比
    31. t1.start();
    32. t2.start();
    33. // 让当前线程睡眠1秒
    34. Thread.sleep(1000);
    35. // 获取线程状态
    36. System.out.println(t1.getState());
    37. System.out.println(t2.getState());
    38. }
    39. }

    1. //Object 类
    2. // wait(long)
    3. // wait()
    4. //TIMED WAITING计时等待
    5. //执行sleep(long millis)等方法 不会释放锁资源
    6. //执行wait(long millis)等方法 会释放所资源
    7. //执行 join(等待时间)
    8. public class TimeWaitingState02 {
    9. public static void main(String[] args) throws InterruptedException {
    10. Thread thread = new Thread() {
    11. @Override
    12. public void run() {
    13. Thread t1 = new Thread() {
    14. @Override
    15. public void run() {
    16. System.out.println("t1线程");
    17. };
    18. };
    19. t1.start();
    20. //join方法会让当前的线程处于计时等待
    21. try {
    22. t1.join(500 * 1000);
    23. } catch (InterruptedException e) {
    24. // TODO Auto-generated catch block
    25. e.printStackTrace();
    26. }
    27. };
    28. };
    29. thread.start();
    30. Thread.sleep(1000);
    31. //查看thread线程的状态
    32. System.out.println(thread.getState());
    33. }
    34. }

    6.  Waiting:等待状态

    运行中的线程,因为join()等方法调用,进入等待;

    1. //WAITING 等待状态 wait()
    2. // join()
    3. //Oject中的wait() wait(long time)
    4. //1.synchronized写在一起
    5. //2.锁对象要和调用的对象保持一致
    6. public class WaitingState01 {
    7. public static void main(String[] args) throws InterruptedException {
    8. Object obj = new Object();
    9. Thread t1 = new Thread() {
    10. public void run() {
    11. synchronized (obj) {
    12. try {
    13. obj.wait();
    14. } catch (InterruptedException e) {
    15. // TODO Auto-generated catch block
    16. e.printStackTrace();
    17. }
    18. }
    19. };
    20. };
    21. t1.start();
    22. Thread.sleep(1000);
    23. System.out.println(t1.getState());//WAITING
    24. }
    25. }

    1. //Oject中的wait() wait(long time)
    2. // 1.synchronized写在一起
    3. // 2.锁对象要和调用的对象保持一致
    4. public class WaitingState02 {
    5. public static void main(String[] args) throws InterruptedException {
    6. Thread t1 = new Thread() {
    7. @Override
    8. public void run() {
    9. while (true)
    10. ;
    11. }
    12. };
    13. t1.start();
    14. t1.join();//t1加到当前的主线程的前面,主线程一直处于等待状态
    15. //获取不到,因为当前的主线程被t1线程插队,t1线程任务是个死循环,无法结束
    16. System.out.println(Thread.currentThread().getState());
    17. }
    18. }

    五、线程池

    1. 什么是线程池?

    线程池内部维护了若干个线程,没有任务的时候,这些线程都处于等待空闲状态。如果有新的线程任务,就分配一个空闲线程执行。如果所有线程都处于忙碌状态,线程池会创建一个新线程进行处理或者放入队列(工作队列)中等待。

    线程池:线程池是一个可以复用线程的技术

    2. 线程池常用类和接口

    在Java标准库提供了如下几个类或接口,来操作并使用线程池:

    1. ExecutorService接口:进行线程池的操作访问;
    2. Executors类:创建线程池的工具类;
    3. ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;

    MyRunable类:

    1. public class MyRunable implements Runnable {
    2. public MyRunable() {
    3. }
    4. public MyRunable(String name) {
    5. }
    6. @Override
    7. public void run() {
    8. // TODO Auto-generated method stub
    9. }
    10. }

    线程池的基本使用方式:

    1. //线程池:线程池是一个可以复用线程的技术
    2. //1. ExecutorService接口:进行线程池的操作访问;
    3. // submit()
    4. // execute()
    5. //2. Executors类:创建线程池的工具类;
    6. // Executors.newCachedThreadPool() 创建一个线程没有上线的线程池
    7. //3. ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;
    8. public class Demo01 {
    9. public static void main(String[] args) throws InterruptedException {
    10. // 创建一个线程没有上限的线程池
    11. ExecutorService pool1 = Executors.newCachedThreadPool();
    12. // 线程池提交任务
    13. pool1.execute(new MyRunable());
    14. // Thread.sleep(1000);
    15. pool1.execute(new MyRunable());
    16. // Thread.sleep(1000);
    17. pool1.execute(new MyRunable());
    18. // Thread.sleep(1000);
    19. pool1.execute(new MyRunable());
    20. // 销毁线程--用完后线程池给砸了
    21. pool1.shutdown();
    22. }
    23. }

    3. 线程池常见方法

    • 执行无返回值的线程任务:void execute(Runnable command);
    • 提交有返回值的线程任务:Future submit(Callable task);
    • 关闭线程池:void shutdown(); 或 shutdownNow();
    • 等待线程池关闭:boolean awaitTermination(long timeout, TimeUnit unit);

    执行线程任务

            execute()只能提交Runnable类型的任务,没有返回值,而submit()既能提交Runnable类型任务也能提交Callable类型任务,可以返回Future类型结果,用于获取线程任务执行结果。
            execute()方法提交的任务异常是直接抛出的,而submit()方法是捕获异常,当调用Future的get()方法获取返回值时,才会抛出异常。

    1. //线程池:线程池是一个可以复用线程的技术
    2. //1. ExecutorService接口:进行线程池的操作访问;
    3. // submit()--提交任务 Runable Callable
    4. // execute()--提交任务Runable
    5. //2. Executors类:创建线程池的工具类;
    6. // Executors.newCachedThreadPool() 创建一个线程没有上限的线程池
    7. // Executors.newFixedThreadPool(int nThreads) 创建一个有上限的线程池
    8. //3. ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;
    9. public class Demo02 {
    10. public static void main(String[] args) {
    11. // Executors.newFixedThreadPool(int nThreads) 创建一个有上限的线程池
    12. ExecutorService pool = Executors.newFixedThreadPool(5);
    13. pool.submit(new MyRunable("任务1"));
    14. pool.submit(new MyRunable("任务2"));
    15. pool.submit(new MyRunable("任务3"));
    16. pool.submit(new MyRunable("任务4"));
    17. }
    18. }

    MyCallable类:

    1. public class MyCallable implements Callable{
    2. private int number;
    3. public MyCallable(int number) {
    4. this.number = number;
    5. }
    6. @Override
    7. public Integer call() throws Exception {
    8. int sum = 0;
    9. for (int i = 0; i <= number ; i++) {
    10. sum+=i;
    11. }
    12. return sum;
    13. }
    14. }
    1. //线程池:线程池是一个可以复用线程的技术
    2. //1. ExecutorService接口:进行线程池的操作访问;
    3. // submit()--提交任务 Runable Callable
    4. // execute()--提交任务Runable
    5. //2. Executors类:创建线程池的工具类;
    6. // Executors.newCachedThreadPool() 创建一个线程没有上线的线程池
    7. // Executors.newFixedThreadPool(int nThreads) 创建一个有上线的线程池
    8. // 核心线程数量到底配置多少:计算密集型任务: =CPU的核数+1; IO密集型任务: CPU的核数*2
    9. //3. ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;
    10. public class Demo03 {
    11. public static void main(String[] args) throws InterruptedException, ExecutionException {
    12. // 创建一个有上线的线程的线程池
    13. ExecutorService pool = Executors.newFixedThreadPool(5);
    14. //测试submit的方法--传callable值
    15. Future futureTask1 = pool.submit(new MyCallable(10));
    16. Future futureTask2 = pool.submit(new MyCallable(100));
    17. Future futureTask3 = pool.submit(new MyCallable(1000));
    18. System.out.println(futureTask1.get());//55
    19. System.out.println(futureTask2.get());//5050
    20. System.out.println(futureTask3.get());//500500
    21. }
    22. }

    4. 线程池分类总结

    FixedThreadPool

    线程数固定的线程池

    线程池参数:

    • 核心线程数和最大线程数一致
    • 非核心线程线程空闲存活时间,即keepAliveTime为0
    • 阻塞队列为无界队列LinkedBlockingQueue

    工作机制:

    1. 提交线程任务
    2. 如果线程数少于核心线程,创建核心线程执行任务
    3. 如果线程数等于核心线程,把任务添加到LinkedBlockingQueue阻塞队列
    4. 如果线程执行完任务,去阻塞队列取任务,继续执行

    使用场景: 适用于处理CPU密集型的任务,确保CPU在长期被工作线程使用的情况下,尽可能的少的分配线程,即适用执行长期的任务。

    CachedThreadPool

    可缓存线程池,线程数根据任务动态调整的线程池
    线程池参数:

    • 核心线程数为0
    • 最大线程数为Integer.MAX_VALUE
    • 工作队列是SynchronousQueue同步队列
    • 非核心线程空闲存活时间为60秒

    ●工作机制:

    1. 提交线程任务
    2. 因为核心线程数为0,所以任务直接加到SynchronousQueue工作队列
    3. 判断是否有空闲线程,如果有,就去取出任务执行
    4. 如果没有空闲线程,就新建一个线程执行
    5. 执行完任务的线程,还可以存活60秒,如果在这期间,接到任务,可以继续存活下去;否则,被销毁。

    使用场景: 用于并发执行大量短期的小任务。

    SingleThreadExecutor

    单线程化的线程池
    线程池参数:

    • 核心线程数为1
    • 最大线程数也为1
    • 阻塞队列是LinkedBlockingQueue
    • 非核心线程空闲存活时间为0秒

    使用场景: 适用于串行执行任务的场景,将任务按顺序执行。

    ScheduledThreadPool

    能实现定时、周期性任务的线程池
    线程池参数:

    • 最大线程数为Integer.MAX_VALUE
    • 阻塞队列是DelayedWorkQueue
    • keepAliveTime为0

    使用场景: 周期性执行任务,并且需要限制线程数量的需求场景。

    1. //线程池:线程池是一个可以复用线程的技术
    2. //1. ExecutorService接口:进行线程池的操作访问;
    3. // submit()
    4. // execute()
    5. //2. Executors类:创建线程池的工具类;
    6. // Executors.newCachedThreadPool() 创建一个线程没有上线的线程池,如果任务执行完线程空闲60秒会被回收
    7. // Executors.newFixedThreadPool(int nThreads) 创建一个有上线的线程池
    8. // Executors.newSingleThreadExecutor()创建一个只有一个线程的线程池对象
    9. // Executors.newScheduledThreadPool() 创建一个线程池,可以实现在给时间延后执行或者定期执行任务
    10. //3. ThreadPoolExecutor及其子类:封装线程池的核心参数和运行机制;
    11. public class Demo04 {
    12. public static void main(String[] args) {
    13. // 创建只有一个线程的线程池
    14. // ExecutorService pool=Executors.newSingleThreadExecutor();
    15. // pool.submit(new MyRunable("任务1"));
    16. // pool.submit(new MyRunable("任务2"));
    17. //1.创建任务调度线程池对象
    18. ScheduledExecutorService pool1=Executors.newScheduledThreadPool(2);
    19. Runnable run = new Runnable() {
    20. @Override
    21. public void run() {
    22. System.out.println(Thread.currentThread().getName());
    23. }
    24. };
    25. //提交任务,延时5秒执行
    26. // pool1.schedule(run,5,TimeUnit.SECONDS);
    27. //提交任务,延时5秒,以后每1秒执行1次
    28. pool1.scheduleAtFixedRate(run, 5, 1,TimeUnit.SECONDS);
    29. }
    30. }

    5. 线程池的配置参数

    ●corePoolSize线程池核心线程数:也可以理解为线程池维护的最小线程数量,核心线程创建后不会被回收。大于核心线程数的线程,在空闲时间超过keepAliveTime后会被回收;

    • 在创建了线程池后,默认情况下,线程池中并没有任何线程,当调用 execute() 方法添加一个任务时,如果正在运行的线程数量小于corePoolSize,则马上创建新线程并运行这个任务。
    • IO密集计算:由于 I/O 设备的速度相对于 CPU来说都很慢,所以大部分情况下,I/O 操作执行的时间相对于 CPU 计算来说都非常长,这种场景我们一般都称为 I/O 密集型计算。最佳线程数 =CPU 核数 * [ 1 +(I/O 耗时 / CPU 耗时)]。
    • CPU密集型:CPU 密集型计算大部分场景下都是纯 CPU 计算,多线程主要目的是提升CPU利用率,最佳线程数 =“CPU 核心数 +1”。这样的话,当线程因为偶尔的内存页失效或其他原因导致阻塞时,这个额外的线程可以临时替补,从而保证 CPU 的利用率。

    ●maximumPoolSize线程池最大线程数:线程池允许创建的最大线程数量;(包含核心线程池数量)
    ●keepAliveTime非核心线程线程存活时间:当一个可被回收的线程的空闲时间大于keepAliveTime,就会被回收。

    • 当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会被回收,直到线程池中的线程数不超过corePoolSize。
    • 如果设置allowCoreThreadTimeOut = true,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;

    ●TimeUnit时间单位:参数keepAliveTime的时间单位;
    ●BlockingQueue阻塞工作队列:用来存储等待执行的任务;
    ●ThreadFactory线程工厂 : 用于创建线程,以及自定义线程名称,需要实现ThreadFactory接口;
    ●RejectedExecutionHandler拒绝策略:当线程池线程内的线程耗尽,并且工作队列达到已满时,新提交的任务,将使用拒绝策略进行处理;

    • ThreadPoolExecutor.AbortPolicy:默认策略,丢弃任务并抛出RejectedExecutionException异常;
    • ThreadPoolExecutor.DiscardPolicy:丢弃任务,但是不抛出异常;
    • ThreadPoolExecutor.DiscardOldestPolicy:丢弃工作队列中的队头任务(即最旧的任务,也就是最早进入队列的任务)后,继续将当前任务提交给线程池;
    • ThreadPoolExecutor.CallerRunsPolicy:由原调用线程处理该任务 (谁调用,谁处理);

    MyRun 类:

    1. public class MyRun implements Runnable {
    2. private String name;
    3. public MyRun() {
    4. }
    5. public MyRun(String name) {
    6. this.name = name;
    7. }
    8. @Override
    9. public void run() {
    10. System.out.println(Thread.currentThread().getName()+ name + "======>" + "正在执行");
    11. // 让任务稍微慢点,便于观察结果
    12. try {
    13. Thread.sleep(Integer.MAX_VALUE);
    14. } catch (InterruptedException e) {
    15. // TODO Auto-generated catch block
    16. e.printStackTrace();
    17. }
    18. }
    19. }
    1. //线程池的创建方式2:
    2. // new ThreadPoolExecutor
    3. public class Demo01 {
    4. public static void main(String[] args) {
    5. // corePoolSize:线程核心数量
    6. // maximumPoolSize:线程池中最大的线程数
    7. // keepAliveTime:临时线程的存活时间
    8. // unit,时间单位(时,分,秒)
    9. // workQueue, 任务队列
    10. // threadFactory, 为线程池创建线程的线程工厂
    11. // handler:指定线程池的拒绝策略
    12. // 1.AbortPolicy--丢弃任务并抛出异常,默认拒绝策略
    13. // 2.CallerRunsPolicy--由主线程负责调用run方法,绕过线程池
    14. // 3.DiscardOldestPolicy--抛弃队列中等待最久的任务,把当前的任务加入到队列
    15. // 4.DiscardPolicy--丢弃任务,不抛出异常,不推荐
    16. ExecutorService pool = new ThreadPoolExecutor(
    17. 3, 6, 60,
    18. TimeUnit.SECONDS, new ArrayBlockingQueue(3),
    19. Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
    20. // MyRun run = new MyRun();
    21. pool.execute(new MyRun("任务1"));//提交第一个任务,线程池会自动创建一个新的线程,自动处理任务
    22. pool.execute(new MyRun("任务2"));
    23. pool.execute(new MyRun("任务3"));
    24. pool.execute(new MyRun("任务4"));//提交第4个任务时候,进入等待队列中
    25. pool.execute(new MyRun("任务5"));
    26. pool.execute(new MyRun("任务6"));
    27. pool.execute(new MyRun("任务7"));//提交第7个任务的时候,核心线程数已满,队列已满,再来新任务,看临时工
    28. pool.execute(new MyRun("任务8"));//
    29. pool.execute(new MyRun("任务9"));
    30. pool.execute(new MyRun("任务10"));//核心线程满,队列满,临时满
    31. pool.execute(new MyRun("任务11"));
    32. }
    33. }

    1. //线程池的创建方式2:
    2. // new ThreadPoolExecutor
    3. public class Demo02 {
    4. public static void main(String[] args) throws InterruptedException, ExecutionException {
    5. // corePoolSize:线程核心数量
    6. // maximumPoolSize:线程池中最大的线程数
    7. // keepAliveTime:临时线程的存活时间
    8. // unit,时间单位(时,分,秒)
    9. // workQueue, 任务队列
    10. // threadFactory, 为线程池创建线程的线程工厂
    11. // handler:指定线程池的拒绝策略
    12. // 1.AbortPolicy--丢弃任务并抛出异常,默认拒绝策略
    13. // 2.CallerRunsPolicy--由主线程负责调用run方法,绕过线程池
    14. // 3.DiscardOldestPolicy--抛弃队列中等待最久的任务,把当前的任务加入到队列
    15. // 4.DiscardPolicy--丢弃任务,不抛出异常,不推荐
    16. ExecutorService pool = new ThreadPoolExecutor(3, 6, 60, TimeUnit.SECONDS, new ArrayBlockingQueue(3),
    17. Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
    18. Future f1=pool.submit(new MyCall(10));
    19. Future f2=pool.submit(new MyCall(20));
    20. Future f3=pool.submit(new MyCall(30));
    21. System.out.println(f1.get());
    22. System.out.println(f2.get());
    23. System.out.println(f3.get());
    24. }
    25. }
    26. class MyCall implements Callable {
    27. private int number;
    28. public MyCall(int number) {
    29. this.number = number;
    30. }
    31. @Override
    32. public Integer call() throws Exception {
    33. int sum = 0;
    34. for (int i = 0; i <= number; i++) {
    35. sum += i;
    36. }
    37. return sum;
    38. }
    39. }

    1. public class Demo03 {
    2. public static void main(String[] args) {
    3. // corePoolSize:线程核心数量
    4. // maximumPoolSize:线程池中最大的线程数
    5. // keepAliveTime:临时线程的存活时间
    6. // unit,时间单位(时,分,秒)
    7. // workQueue, 任务队列
    8. // threadFactory, 为线程池创建线程的线程工厂
    9. // handler:指定线程池的拒绝策略
    10. // 1.AbortPolicy--丢弃任务并抛出异常,默认拒绝策略
    11. // 2.CallerRunsPolicy--由主线程负责调用run方法,绕过线程池
    12. // 3.DiscardOldestPolicy--抛弃队列中等待最久的任务,把当前的任务加入到队列
    13. // 4.DiscardPolicy--丢弃任务,不抛出异常,不推荐
    14. ThreadPoolExecutor pool = new ThreadPoolExecutor(
    15. 3, 6, 60,
    16. TimeUnit.SECONDS, new ArrayBlockingQueue(3),
    17. new MyThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
    18. // MyRun run = new MyRun();
    19. pool.execute(new MyRun("任务1"));//提交第一个任务,线程池会自动创建一个新的线程,自动处理任务
    20. pool.execute(new MyRun("任务2"));
    21. pool.execute(new MyRun("任务3"));
    22. ThreadPoolExecutor pool1 = new ThreadPoolExecutor(
    23. 3, 6, 60,
    24. TimeUnit.SECONDS, new ArrayBlockingQueue(3),
    25. new MyThreadFactory(), new ThreadPoolExecutor.DiscardPolicy());
    26. // MyRun run = new MyRun();
    27. pool1.execute(new MyRun("任务1"));//提交第一个任务,线程池会自动创建一个新的线程,自动处理任务
    28. pool1.execute(new MyRun("任务2"));
    29. pool1.execute(new MyRun("任务3"));
    30. }
    31. }
    32. class MyThreadFactory implements ThreadFactory {
    33. private static final AtomicInteger poolNumber = new AtomicInteger(1);
    34. private final ThreadGroup group;
    35. private final AtomicInteger threadNumber = new AtomicInteger(1);
    36. private final String namePrefix;
    37. MyThreadFactory() {
    38. SecurityManager s = System.getSecurityManager();
    39. group = (s != null) ? s.getThreadGroup() :
    40. Thread.currentThread().getThreadGroup();
    41. namePrefix = "池子-" +
    42. poolNumber.getAndIncrement() +
    43. "-线程-";
    44. }
    45. public Thread newThread(Runnable r) {
    46. Thread t = new Thread(group, r,
    47. namePrefix + threadNumber.getAndIncrement(),
    48. 0);
    49. if (t.isDaemon())
    50. t.setDaemon(false);
    51. if (t.getPriority() != Thread.NORM_PRIORITY)
    52. t.setPriority(Thread.NORM_PRIORITY);
    53. return t;
    54. }
    55. }

    6. 线程池的执行流程

    1. 提交一个新线程任务,线程池会在线程池中分配一个空闲线程,用于执行线程任务;
    2. 如果线程池中不存在空闲线程,则线程池会判断当前“存活的线程数”是否小于核心线程数corePoolSize。

            ○如果小于核心线程数corePoolSize,线程池会创建一个新线程(核心线程)去处理新线程任务;
            ○如果大于核心线程数corePoolSize,线程池会检查工作队列;


                    ■如果工作队列未满,则将该线程任务放入工作队列进行等待。线程池中如果出现空闲线程,将从工作队列中按照FIFO的规则取出1个线程任务并分配执行;
                    ■如果工作队列已满,则判断线程数是否达到最大线程数maximumPoolSize;

                            ●如果当前“存活线程数”没有达到最大线程数maximumPoolSize,则创建一个新线程(非核心线程)执行新线程任务;
                    ●如果当前“存活线程数”已经达到最大线程数maximumPoolSize,直接采用拒绝策略处理新线程任务;

    综上所述,执行顺序为:核心线程、工作队列、非核心线程、拒绝策略

    7. 线程池的状态

    线程池的状态分为:RUNNING,SHUTDOWN,STOP,TIDYING,TERMINATED


    RUNNING 运行状态,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0。该状态的线程池会接收新任务,并处理工作队列中的任务。
    • 调用线程池的shutdown()方法,可以切换到SHUTDOWN关闭状态;
    • 调用线程池的shutdownNow()方法,可以切换到STOP停止状态;

    SHUTDOWN关闭状态,该状态的线程池不会接收新任务,但会处理工作队列中的任务;

    • 当工作队列为空时,并且线程池中执行的任务也为空时,线程池进入TIDYING状态;

    STOP停止状态,该状态的线程不会接收新任务,也不会处理阻塞队列中的任务,而且会中断正在运行 的任务;

    • 线程池中执行的任务为空,进入TIDYING状态;

    TIDYING整理状态,该状态表明所有的任务已经运行终止,记录的任务数量为0;

    • terminated()执行完毕,进入TERMINATED状态;

    TERMINATED: 终止状态,该状态表示线程池彻底关闭。

  • 相关阅读:
    开发工程师必备————【Day15】python操作Mysql及SQL语法补充
    kafka、zookeeper、flink测试环境、docker
    前端进击笔记第二十四节 如何进行技术方案调研与设计?
    STM32实战总结:HAL之I2C
    [Java框架] Java常用爬虫框架推荐
    在Android工程中的.gitignore对.gradle目录失效问题解决
    如何清理mac磁盘空间?该删不该删的你都知道了吗
    springboot 老年人健康保障管理系统毕业设计源码302303
    C#面:关于集合的几个选择题
    餐饮美食网页设计(HTML+CSS+JavaScript)
  • 原文地址:https://blog.csdn.net/qq_52897007/article/details/139410396