• Java 并发 -- 线程状态(Java线程六种状态、操作系统层次的五种状态)、线程池(七大核心参数)、wait vs sleep



    1. 线程状态

    要求

    • 掌握 Java 线程六种状态
    • 掌握 Java 线程状态转换
    • 能理解五种状态与六种状态两种说法的区别

    1.1 Java线程六种状态及转换

    在这里插入图片描述
    分别是

    • 新建
      • 当一个线程对象被创建,但还未调用 start 方法时处于新建状态
      • 此时未与操作系统底层线程关联
    • 可运行
      • 调用了 start 方法,就会由新建进入可运行
      • 此时与底层线程关联,由操作系统调度执行
    • 终结
      • 线程内代码已经执行完毕,由可运行进入终结
      • 此时会取消与底层线程关联
    • 阻塞
      • 当获取锁失败后,由可运行进入 Monitor 的阻塞队列阻塞,此时不占用 cpu 时间
      • 当持锁线程释放锁时,会按照一定规则唤醒阻塞队列中的阻塞线程,唤醒后的线程进入可运行状态
    • 等待
      • 当获取锁成功后,但由于条件不满足,调用了 wait() 方法,此时从可运行状态释放锁进入 Monitor 等待集合等待,同样不占用 cpu 时间
      • 当其它持锁线程调用 notify() 或 notifyAll() 方法,会按照一定规则唤醒等待集合中的等待线程,恢复为可运行状态
    • 有时限等待
      • 当获取锁成功后,但由于条件不满足,调用了 wait(long) 方法,此时从可运行状态释放锁进入 Monitor 等待集合进行有时限等待,同样不占用 cpu 时间
      • 当其它持锁线程调用 notify() 或 notifyAll() 方法,会按照一定规则唤醒等待集合中的有时限等待线程,恢复为可运行状态,并重新去竞争锁
      • 如果等待超时,也会从有时限等待状态恢复为可运行状态,并重新去竞争锁
      • 还有一种情况是调用 sleep(long) 方法也会从可运行状态进入有时限等待状态,但与 Monitor 无关,不需要主动唤醒,超时时间到自然恢复为可运行状态

    其它情况(只需了解)

    • 可以用 interrupt() 方法打断等待有时限等待的线程,让它们恢复为可运行状态
    • park,unpark 等方法也可以让线程等待和唤醒

    1.2 操作系统层次的五种状态

    这是从 操作系统 层面来描述的
    在这里插入图片描述

    • 【初始状态】仅是在语言层面创建了线程对象,还未与操作系统线程关联

    • 【可运行状态】(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由 CPU 调度执行

    • 【运行状态】指获取了 CPU 时间片运行中的状态

      • 当 CPU 时间片用完,会从【运行状态】转换至【可运行状态】,会导致线程的上下文切换
    • 【阻塞状态】

      • 如果调用了阻塞 API,如 BIO 读写文件,这时该线程实际不会用到 CPU,会导致线程上下文切换,进入【阻塞状态】
      • 等 BIO 操作完毕,会由操作系统唤醒阻塞的线程,转换至【可运行状态】
      • 与【可运行状态】的区别是,对【阻塞状态】的线程来说只要它们一直不唤醒,调度器就一直不会考虑调度它们
    • 【终止状态】表示线程已经执行完毕,生命周期已经结束,不会再转换为其它状态


    2. 线程池(七大核心参数)

    要求

    • 掌握线程池的 7 大核心参数

    七大参数

    1. corePoolSize 核心线程数目 - 池中会保留的最多线程数
    2. maximumPoolSize 最大线程数目 - 核心线程+救急线程的最大数目
    3. keepAliveTime 生存时间 - 救急线程的生存时间,生存时间内没有新任务,此线程资源会释放
    4. unit 时间单位 - 救急线程的生存时间单位,如秒、毫秒等
    5. workQueue - 当没有空闲核心线程时,新来任务会加入到此队列排队,队列满会创建救急线程执行任务
    6. threadFactory 线程工厂 - 可以定制线程对象的创建,例如设置线程名字、是否是守护线程等
    7. handler 拒绝策略 - 当所有线程都在繁忙,workQueue 也放满时,会触发拒绝策略
      1. workQueue满了,救急线程数目也达到最大了,后面来任务直接抛异常 java.util.concurrent.ThreadPoolExecutor.AbortPolicy
        在这里插入图片描述

      2. workQueue满了,救急线程数目也达到最大了,新加的任务由调用者执行(谁去submit任务谁就去执行任务) java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy
        在这里插入图片描述

      3. workQueue满了,救急线程数目也达到最大了,后面来的任务之间丢弃。java.util.concurrent.ThreadPoolExecutor.DiscardPolicy
        在这里插入图片描述

      4. workQueue满了,救急线程数目也达到最大了,直接丢弃最早排队任务 (丢弃排队时间最长的任务,把新的任务添加到workQueue)java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy
        在这里插入图片描述

    示例代码:

    在这里插入图片描述

    import org.slf4j.Logger;
    
    import java.lang.reflect.Field;
    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.*;
    import java.util.concurrent.atomic.AtomicInteger;
    import java.util.stream.Collectors;
    import java.util.stream.IntStream;
    
    import static day02.LoggerUtils.*;
    
    public class TestThreadPoolExecutor {
    
        public static void main(String[] args) throws InterruptedException {
            AtomicInteger c = new AtomicInteger(1);
            ArrayBlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
            ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
                    2,
                    3,
                    0,
                    TimeUnit.MILLISECONDS,
                    queue,
                    r -> new Thread(r, "myThread" + c.getAndIncrement()),
                    new ThreadPoolExecutor.DiscardOldestPolicy());
            showState(queue, threadPool);
            threadPool.submit(new MyTask("1", 3600000));
            showState(queue, threadPool);
            threadPool.submit(new MyTask("2", 3600000));
            showState(queue, threadPool);
            threadPool.submit(new MyTask("3"));
            showState(queue, threadPool);
            threadPool.submit(new MyTask("4"));
            showState(queue, threadPool);
            threadPool.submit(new MyTask("5", 3600000));
            showState(queue, threadPool);
            threadPool.submit(new MyTask("6"));
            showState(queue, threadPool);
        }
    
        private static void showState(ArrayBlockingQueue<Runnable> queue, ThreadPoolExecutor threadPool) {
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            List<Object> tasks = new ArrayList<>();
            for (Runnable runnable : queue) {
                try {
                    Field callable = FutureTask.class.getDeclaredField("callable");
                    callable.setAccessible(true);
                    Object adapter = callable.get(runnable);
                    Class<?> clazz = Class.forName("java.util.concurrent.Executors$RunnableAdapter");
                    Field task = clazz.getDeclaredField("task");
                    task.setAccessible(true);
                    Object o = task.get(adapter);
                    tasks.add(o);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            main.debug("pool size: {}, queue: {}", threadPool.getPoolSize(), tasks);
        }
    
        static class MyTask implements Runnable {
            private final String name;
            private final long duration;
    
            public MyTask(String name) {
                this(name, 0);
            }
    
            public MyTask(String name, long duration) {
                this.name = name;
                this.duration = duration;
            }
    
            @Override
            public void run() {
                try {
                    LoggerUtils.get("myThread").debug("running..." + this);
                    Thread.sleep(duration);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
    
            @Override
            public String toString() {
                return "MyTask(" + name + ")";
            }
        }
    }
    
    • 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
    • 93

    运行结果:
    在这里插入图片描述


    3. wait vs sleep

    要求

    • 能够说出二者区别

    一个共同点,三个不同点

    共同点

    • wait() ,wait(long) 和 sleep(long) 的效果都是让当前线程暂时放弃 CPU 的使用权,进入阻塞状态。

    不同点

    • 方法归属不同

      • sleep(long) 是 Thread 的静态方法
      • 而 wait(),wait(long) 都是 Object 的成员方法,每个对象都有
    • 醒来时机不同

      • 执行 sleep(long) 和 wait(long) 的线程都会在等待相应毫秒后醒来
      • wait(long) 和 wait() 还可以被 notify 唤醒,wait() 如果不唤醒就一直等下去
      • 它们都可以被打断唤醒(调用线程的 interrupt() 方法)
    • 锁特性不同(重点)

      • wait 方法的调用必须先获取 wait 对象的锁,而 sleep 则无此限制
      • wait 方法执行后会释放对象锁,允许其它线程获得该对象锁(我放弃 cpu,但你们还可以用)
      • 而 sleep 如果在 synchronized 代码块中执行,并不会释放对象锁(我放弃 cpu,你们也用不了)

    下面是一些示例代码。


    3.1 wait() 必须配合锁一起使用

        static final Object LOCK = new Object();
    
        public static void main(String[] args) throws InterruptedException {
            LOCK.wait();
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    运行结果:
    在这里插入图片描述

        static final Object LOCK = new Object();
    
        public static void main(String[] args) throws InterruptedException {
            synchronized (LOCK){
                LOCK.wait();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    在这里插入图片描述


    3.2 wait 方法执行后会释放锁 sleep则不会

    示例代码:
    在这里插入图片描述
    运行结果:
    在这里插入图片描述

    示例代码:
    在这里插入图片描述

    运行结果:
    在这里插入图片描述


    3.3 interrupt()方法可以提前唤醒sleep线程和wait线程

    在这里插入图片描述
    运行结果:
    在这里插入图片描述



  • 相关阅读:
    熵-条件熵-联合熵-互信息-交叉熵
    【数据结构常见七大排序(三)上】—交换排序篇【冒泡排序】And【快速排序】
    java基于ssm+vue的婚纱摄影网站
    我理解的游戏数据模型
    k8s:部署k8s单master节点集群(kubeadm)v1.23.13
    Python 实现自动化测试 dubbo 协议接口
    深入理解 JVM(重点:双亲委派模型 + 垃圾回收算法)
    Vue 自定义组件中 v-model 的使用
    【DBAPI教程】DBAPI如何使用复杂多层嵌套JSON作为请求参数
    史上最全架构师知识图谱(纯干货)
  • 原文地址:https://blog.csdn.net/I_r_o_n_M_a_n/article/details/125001685