• Java多线程笔记


    在这里插入图片描述

    给自己一个希望,不为昨天烦恼,不为明天迷惘,只为今天更美好;


    一、概述


    1、什么是进程?

    • 可以把进程理解成一个在内存中运行的应用程序。

    • 每个进程都有自己独立的一块内存空间。

    • 进程不依赖于线程而独立存在,一个进程中可以启动多个线程。

    例如 当我们点击IDEA.exe可执行文件时,后台便会在内存中创建一个进程。


    2、什么是线程?

    • 线程 (thread) 是进程的一个子程序(模块)。

    • 线程是进程的一条执行路径/执行单元。

    • 线程也有独立的内存空间。


    3、并行、并发

    并行: 同一时刻,多个程序同时执行。


    并发: 同一时间段,多个程序交替执行。


    4、线程调度的原理

    • 分时调度:为每个线程分配相同的执行时间。
    • 抢占式调度:CPU优先执行优先级别高的线程,多个相同优先级的线程,CPU就随机选择一个执行。
    • 线程执行需要CPU资源,多个线程抢夺CPU资源,谁抢到谁就先执行。

    5、什么是多线程?

    • 多进程是指操作系统能同时运行多个任务(应用程序)。

    • 多线程是指一个进程中同时有多个执行路径正在执行(可以将耗时任务放后台执行,同时我们执行其他的操作)。

    单线程:

    在这里插入图片描述

    多线程:

    在这里插入图片描述


    6、多线程用在哪里,有什么好处?

    • 12306、百度网盘、消息通信、淘宝、京东系统等都离不开多线程技术。
    • 好处:可以解决负载均衡问题,充分利用CPU的资源,为了提高CPU的使用率,才用多线程的方式去同时完成几件事情而不互相干扰。

    二、多线程的创建


    方式一、继承Thread类

    步骤如下:

    1、首先定义一个类MyThread,去继承线程类java.lang.Thread,然后重写run()方法。
    2、创建MyThread类的对象。
    3、调用线程对象的start()方法启动线程 (启动后还是执行run方法)

    Thread类的构造器:
    在这里插入图片描述

    public class ThreadDemo1 {
        public static void main(String[] args) {
            // 3、new一个线程类对象
            Thread t = new MyThread();
            // 4、启动线程 (执行的还是run方法)
            t.start();
    
    
            // main方法代表主线程
            for (int i = 0; i < 5 ; i++) {
                System.out.println("主线程执行输出:"+i);
            }       
        }
    }
    
    /**
     * 1、定义一个线程类
     */
    class MyThread extends Thread{
    
        /**
         * 2重写run方法,run方法里面定义线程要干什么
         */
        @Override
        public void run() {
            for (int i = 0; i < 5 ; i++) {
                System.out.println("子线程执行输出:"+i);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    在这里插入图片描述
    方式一的优缺点:

    • 优点:编码简单 。
    • 缺点:存在单线程的局限性,因为Java是单继承的,即线程类继承Thread后,不能继承其他类,不便于扩展。

    问题1、为什么不直接调用了run方法,而是调用start方法启动线程?

    • 直接调用run方法会当成普通方法执行(对象.方法),此时还是相当于单线程执行。

    在这里插入图片描述

    • 只有调用start方法才是启动一个新的线程执行。

    源码分析:

    在这里插入图片描述
    在这里插入图片描述

    start()方法中有个native修饰的start0(),它是本地方法,是JVM调用,底层是c/c++实现。所以说:真正实现多线程的效果,是start0(),而不是 run()

    在这里插入图片描述

    Java 是跨平台的,可以在不同系统上运行,每个系统的 CPU 调度算法不一样,所以就需要做不同的处理,这件事情就只能交给 JVM 来实现了,start0() 方法自然就表标记成了 native


    问题2、为什么不要把主线程任务放在子线程之前?

    • 这样主线程一直是先跑完的,相当于是一个单线程的效果。

    在这里插入图片描述

    总结:

    • 继承Thread类
    • 重写run方法
    • 创建线程对象
    • 调用start()方法启动线程。

    方式二、实现Runnable接口

    步骤如下:

    1、定义一个线程任务类MyRunnable实现Runnable接口,重写run()方法。
    2、创建MyRunnable任务对象。
    3、把MyRunnable任务对象交给Thread(线程)处理。
    4、调用线程对象的start()方法启动线程。

    //线程类
    public class MyThread implements Runnable{
        @Override
        public void run() {
            for (int i = 1; i <= 5; i++) {
                System.out.println(Thread.currentThread().getName()+"---------------"+i);
                // try {
                //     Thread.sleep(1000);
                // } catch (InterruptedException e) {
                //     e.printStackTrace();
                // }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    // 测试类
    public class Demo01Runnable {
        public static void main(String[] args) {
            Thread thread1 = new Thread(new MyRunnable());
            Thread thread2 = new Thread(new MyRunnable());
            Thread thread3 = new Thread(new MyRunnable());
            
            thread1.setName("我是线程一");
            thread2.setName("我是线程二");
            thread3.setName("我是线程小三");
    
            thread1.start();
            thread2.start();
            thread3.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    底层执行原理:

    // Runnable接口源码
    @FunctionalInterface
    public interface Runnable {
        /**
         * When an object implementing interface Runnable is used
         * to create a thread, starting the thread causes the object's
         * run method to be called in that separately executing
         * thread.
         * 

    * The general contract of the method run is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }

    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    // Thread类部分源码
    public class Thread implements Runnable {
        /* What will be run. */
        private Runnable target; //用于接收Runnable接口实现类对象
         
         //无参构造器
         public Thread() {
            init(null, null, "Thread-" + nextThreadNum(), 0);
        }
    	
    	// 有参构造器,参数为:Runnable 接口实现类
    	 public Thread(Runnable target) {
            init(null, target, "Thread-" + nextThreadNum(), 0);
        }
    	
    	// 用当前的AccessControlContext初始化一个线程
    	private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
            init(g, target, name, stackSize, null, true);
        }
        
    	// 初始化一个线程
    	private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,boolean inheritThreadLocals) {
    	     ...
    	     this.target = target; // 将Runnable 接口实现类对象赋值给成员变量
    	     ...
    	 }
    
        // 启动线程的方法
        public synchronized void start() {
    	   start0(); 
           ...
       }
    
       // start0是本地方法,底层是C++实现的,然后调用系统资源,以多线程的方式调用run()方法
       private native void start0();	
    
    	// 在run()方法中指定线程要执行的任务
    	// 又因为Runnable接口实现类重写了这个方法,所以他走Runnable接口实现类的run()方法
        @Override
        public void run() {
            if (target != null) {
                target.run();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45

    方式二的优缺点:

    • 优点:线程任务类只是实现接口,可以继续继承类和实现其他接口,扩展性强。
    • 缺点:编程多了一层对象包装,如果线程有执行结果是不能直接返回的。(void修饰的方法)

    实现Runnable接口(匿名内部类方式):

    1、可以创建Runnable的匿名内部类对象。

    2、交给Thread处理。

    3、调用线程对象的start()方法启动线程。

    public class ThreadDemo3 {
        public static void main(String[] args) {
    
            // 1、创建Runnable的匿名内部类对象
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    System.out.println("子线程启动1");
                }
            };
            // 2、交给Thread处理
            Thread t = new Thread(runnable);
            // 3、启动线程
            t.start();
    
    
            /*
                第二种写法:通过Thread构造器入参匿名内部类方式创建
             */
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println("子线程启动2");
                }
            }).start();
    
    
    
            /*
                  第三种写法:使用lambda表达式简化内部类
             */
            new Thread(() -> {
                System.out.println("子线程启动3");
            }).start();
    
    
            // main方法代表主线程
            for (int i = 0; i < 5; i++) {
                System.out.println("主线程执行输出:" + i);
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    方式三、实现Callable接口 ( JDK5.0新增的)

    1、前两种线程创建方式都存在一个问题:

    • 他们重写的run方法均不能直接返回结果。(void修饰)
    • 不适合需要返回线程执行结果的业务场景。

    2、怎么解决这个问题?

    • JDK5.0提供了Callable和FutureTask来实现。

    3、实现Callable接口、结合FutureTask完成线程创建,步骤如下:

    (1) 得到任务对象。

    • 定义一个类去实现Callable接口,重写call方法,封装要做的事情。
    • 用FutureTask把Callable对象封装成线程对象。

    (2) 把线程任务对象交给Thread处理。

    (3) 调用Thread的start方法启动线程,执行任务。


    FutureTask的API:

    方法功能
    public FutureTask​(Callable call)把Callable对象封装成 FutureTask 对象
    public V get()获取线程执行call方法返回的结果
    import java.util.concurrent.Callable;
    import java.util.concurrent.FutureTask;
    
    /**
     * 实现Callable接口、结合FutureTask完成线程创建
     *
     * @author 白豆五
     * @version 2022/11/13 19:26
     * @since JDK8
     */
    public class ThreadDemo4 {
        public static void main(String[] args) {
            // 3、创建Callable任务对象
            Callable<String> call1 = new MyCallable(10);
    
            // 4、把Callable任务对象交给FutureTask对象
            /*
                FutureTask的作用:
                    1、它是Runnable接口实现类对象,可以交给Thread对象
                    2、可以在线程执行完毕后,通过调用FutureTask的get方法得到线程执行完毕的结果
             */
            FutureTask<String> f1 = new FutureTask<String>(call1);
    
            // 5、交给线程处理
            Thread t1 = new Thread(f1);
    
            // 6、启动线程
            t1.start();
    
    
    
            /*
                 再次启动线程
             */
            Callable<String> call2 = new MyCallable(100);
            FutureTask<String> f2 = new FutureTask<String>(call2);
            Thread t2 = new Thread(f2);
            t2.start();
    
    
            try {
                // 如果f1任务没有执行完毕,这里的代码会等待,直到线程1跑完才提取结果。
                String res1 = f1.get();
                System.out.println("第一个结果:" + res1);
            } catch (Exception e) {
                e.printStackTrace();
    
            }
    
            try {
                // 如果f2任务没有执行完毕,这里的代码会等待,直到线程2跑完才提取结果。
                String res2 = f2.get();
                System.out.println("第二个结果:" + res2);
    
            } catch (Exception e) {
                e.printStackTrace();
    
            }
        }
    }
    
    /**
     * 1、定义一个任务类,实现Callable接口,应该声明线程任务执行完毕后返回结果的数据类型。
     */
    class MyCallable implements Callable<String> {
    
        private int n;
    
        // 使用构造器,为变量n赋值
        public MyCallable(int n) {
            this.n = n;
        }
    
        /**
         * 2、重写call方法(任务方法)
         *
         * @return
         * @throws Exception
         */
        @Override
        public String call() throws Exception {
            /*
               需求:求1到n的和
             */
            int sum = 0;
            for (int i = 1; i <= n; i++) {
                sum += i;
            }
    
            return "子线程执行的结果是:" + sum;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92

    在这里插入图片描述

    方式三的优缺点:

    • 优点:
      • 线程任务类只是实现接口,可以继续继承类和实现其他接口,扩展性强。
      • 可以在线程执行完毕后去获取线程执行的结果。
    • 缺点:编码复杂一点。

    三种方式的对比:

    定义方式优点缺点
    继承Thread类编程比较简单,可以直接使用Thread类中的方法扩展性较差,不能再继承其他的类,不能返回线程执行的结果
    实现Runnable接口扩展性强,实现该接口的同时还可以继承其他的类编程相对复杂,不能返回线程执行的结果
    实现Callable接口扩展性强,实现该接口的同时还可以继承其他的类。可以得到线程执行的结果编程相对复杂

    三、Thread类的常用方法


    • Thread常用方法:获取线程名称getName()、设置名称setName()、获取当前线程对象currentThread()。
    • 至于Thread类还提供了: yield、join、interrupt、不推荐的方法stop、守护线程、线程优先级等线程的控制方法,在开发中很少使用。

    1、Thread类构造器

    在这里插入图片描述

    2、设置和获取线程的名字

    • String getName() : 返回此线程的名称。
    • void setName(String name) : 修改此线程的名称。

    3、获取当前线程的对象

    在这里插入图片描述

    Thread.currentThread()//获取当前线程的对象	
    
    • 1

    4、线程休眠的方法

    static void sleep​(long millis) : 让当前线程休眠指定的时间后再继续执行,单位为毫秒。


    5、线程任务方法、线程启动方法

    • public void run() :线程任务方法。
    • public void start() :线程启动方法。

    四、线程安全


    线程安全问题:当多个线程同时操作同一个共享资源的时候,可能会出现数据不一致问题,称为线程安全问题。

    线程安全出现的原因: 存在多线程并发,同时访问共享资源,存在修改共享资源。


    示例: 模拟三个窗口共卖100张票

    //1.定义类实现Runnable接口
    public class MySellTicketTask implements Runnable {
    	// 票数
        private int num = 100;
    
        // 2.Runnable接口的实现类,覆盖重写run方法,指定线程任务
        @Override
        public void run() {
            while (true) {
                //(1)判断是否有票
                if (num > 0) {
                    //模拟出票的时间
                    try {Thread.sleep(100);}catch (Exception e){e.printStackTrace();}
                    //(2)有票: 打印输出一张票
                    String threadName = Thread.currentThread().getName();
                    System.out.println(threadName + "卖出第 " + num + " 张票...........");
                    //(3)票的数量减少1张
                    num--;
                }
    
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    public class Demo01Tickets {
        public static void main(String[] args) {
            //3.创建Runnable接口的实现类的对象,线程任务对象
            MySellTicketTask task = new MySellTicketTask();
    
            //4.创建3个线程对象,构造方法发传递线程任务对象
            Thread t1 = new Thread(task,"手机App");
            Thread t2 = new Thread(task,"网站");
            Thread t3 = new Thread(task,"前台");
    
            //5.这3个线程对象分别调用start方法,开启线程
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    五、线程同步


    线程同步:为了解决线程安全问题。

    如何保证线程安全? 让多个线程实现先后依次访问共享资源,这样就解决了安全问题。


    线程同步的核心思想:

    • 加锁,把共享资源进行上锁,每次只能进入一个线程,执行完毕后自动解锁,然后其他线程才能进来。

    在这里插入图片描述


    方式一、同步代码块


    1、同步代码块的作用: 把出现线程安全问题的核心代码给上锁。

    2、同步代码块的原理: 每次只能一个线程进入,执行完毕后自动解锁,其他线程才可以进来执行。

    语法格式如下:

    synchronized(同步锁对象){
    	操作共享资源的代码(核心代码)
    }
    
    • 1
    • 2
    • 3
    • 锁对象要求: 只要对于当前同时执行的线程来说是同一个对象即可。

    3、锁对象用任意唯一的对象好不好呢?

    • 不好,会影响其他无关线程的执行。
    @Override
    public void run() {
        while (true) {
            //使用同步代码块,解决线程安全问题
            synchronized (this) {
                //(判断是否有票
                if (num > 0) {
                    //延时模拟出票的时间
                    try {Thread.sleep(10);}catch (Exception e){e.printStackTrace();}
                    // 打印输出一张票
                    String threadName = Thread.currentThread().getName();
                    System.out.println(threadName + "卖出第 " + num + " 张票...........");
                    //票的数量减少1张
                    num--;
                }
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    4、锁对象的规范要求:

    • 规范上:建议使用 “共享资源” 作为锁对象。
    • 对于 “实例方法” 建议使用this作为锁对象。
    • 对于 “静态方法” 建议使用字节码(类名.class)对象作为锁对象。

    总结

    1、同步代码块是如何实现线程安全的?

    • 对出现问题的核心代码使用synchronized=进行加锁
    • 每次只能一个线程占锁进入访问

    2、同步代码块的同步锁对象有什么要求?

    • 对于 “实例方法” 建议使用this作为锁对象。
    • 对于 “静态方法” 建议使用字节码(类名.class)对象作为锁对象。

    方式二、同步方法


    1、同步方法的作用: 把出现线程安全问题的核心方法给上锁。

    2、同步方法的原理: 每次只能一个线程进入,执行完毕以后自动解锁,其他线程才可以进来执行。

    格式如下:

    修饰符 synchronized 返回值类型 方法名称(形参列表){
    	操作共享资源的代码
    }
    
    • 1
    • 2
    • 3

    示例:

    @Override
    public synchronized void run() {
        while (true) {
           //(判断是否有票
           if (num > 0) {
               //延时模拟出票的时间
               try {Thread.sleep(10);}catch (Exception e){e.printStackTrace();}
               // 打印输出一张票
               String threadName = Thread.currentThread().getName();
               System.out.println(threadName + "卖出第 " + num + " 张票...........");
               //票的数量减少1张
               num--;
           }
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    3、同步方法底层原理:

    • 同步方法其实底层也是有隐式锁对象的,只是锁的范围是整个方法代码。
    • 如果方法是实例方法:同步方法默认用this作为的锁对象。但是代码要高度面向对象!
    • 如果方法是静态方法:同步方法默认用类名.class作为的锁对象。

    总结

    1、同步方法是如何保证线程安全的?

    • 对出现问题的核心方法使用synchronized修饰加锁。
    • 每次只能一个线程占锁进入访问。

    2、同步方法的同步锁对象的原理?

    • 对于实例方法默认使用this作为锁对象。
    • 对于静态方法默认使用类名.class对象作为锁对象。

    方式三、lock锁


    Lock锁概述

    • 为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock,更加灵活、方便。

    • Lock接口在juc包(并发包: java.util.concurrent)里面。

    • Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作。

    • Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来构建Lock锁对象。


    ReentrantLock类的构造器

    在这里插入图片描述

    Lock接口的API

    在这里插入图片描述

    • lock():获取锁;
    • unlock():释放锁;

    /*
        使用Lock锁对象替换同步代码块
     */
    @SuppressWarnings("All")
    public class MySellTicketTask05 implements Runnable {
        //多个线程共享的数据
        private int num = 100;
    
        //使用Lock锁对象替换同步代码块
        //保证锁对象的唯一性
        private Lock lock = new ReentrantLock();//多态
    
        @Override
        public void run() {
            //死循环
            while (true) {
                //获取锁: 上锁 <================
                lock.lock();
    
                //(2)有票: 打印输出一张票
                //模拟出票的时间
                try {
                    //(1)判断是否有票
                    if (num > 0) {
                        Thread.sleep(10);
                        String threadName = Thread.currentThread().getName();
                        System.out.println(threadName+"卖出第 " + num + " 张票....");
                        //(3)票的数量减少1张
                        num--;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    //释放锁  <================
                    lock.unlock();
                }
            }
        }
    }
    //测试类
    public class Demo05Lock {
        public static void main(String[] args) {
            //创建线程任务对象
            MySellTicketTask05 task = new MySellTicketTask05();
            //创建线程对象,传递线程任务对象,指定线程名称
            Thread t1 = new Thread(task, "窗口");
            Thread t2 = new Thread(task, "手机APP");
            Thread t3 = new Thread(task, "网站");
            //调用start方法开启线程
            t1.start();
            t2.start();
            t3.start();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54

    在这里插入图片描述

    六、死锁及线程状态


    线程状态

    线程状态导致状态发生条件
    NEW(新建)线程刚被创建,但是并未启动。还没调用start方法。
    Runnable(可运行)线程可以在java虚拟机中运行的状态,可能正在运行自己代码,也可能没有,这取决于操作系统处理器。
    Blocked(锁阻塞)当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
    Waiting(无限等待)一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒。
    Timed Waiting(计时等待)同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态。这一状态将一直保持到超时期满或者接收到唤醒通知。带有超时参数的常用方法有Thread.sleep 、Object.wait。
    Teminated(被终止)因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡。

    //TODO …

  • 相关阅读:
    JS合并2个远程pdf
    【DW组队学习—动手学数据分析】第二章:第三节数据重构2
    Docker Compose之容器编排开发初探
    LDR6020芯片驱动未来:TYPE-C桌面显示器的新篇章
    文件上传_白名单、内容校验、竞争上传
    丁鹿学堂:前端面试之promise A + 规范
    java毕业设计软件源代码SSM酒店管理系统|旅店管理
    FeignClient 数据请求方式小结
    记LGSVL Map Annotation(3)利用map annotation生成一个十字路口的opendrive格式地图
    如何基于FSM有限状态机实现Enemies AI
  • 原文地址:https://blog.csdn.net/qq_46921028/article/details/127836757