zoukankan      html  css  js  c++  java
  • 04 JAVA中park/unpark的原理以及JAVA在API层面线程状态总结

    1 park与unpark的使用以及原理

    1-1 基本使用

    • park/unpark并非线程类的方法,是concurrent的方法
    // 暂停当前线程
    LockSupport.park();
    // 恢复某个线程的运行
    LockSupport.unpark(暂停线程对象)
    

    实例

    package chapter4;
    import java.util.Hashtable;
    import java.util.Map;
    import java.util.Set;
    import java.util.concurrent.locks.LockSupport;
    import chapter2.Sleeper;
    import lombok.extern.slf4j.Slf4j;
    @Slf4j(topic = "c.test3")
    public class test3 {
        public static void main(String[] args) throws InterruptedException {
            Thread t1 = new Thread(() -> {
                log.debug("start...");
                try {
                    Thread.sleep(1000);                // 睡眠时间1
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                log.warn("park...");
                LockSupport.park();
                log.warn("resume...");
            },"t1");
            t1.start();
            Thread.sleep(2000);
            log.warn("unpark...");                      //睡眠时间2
            LockSupport.unpark(t1);
        }
    }
    

    运行结果1(设置睡眠时间1为1000ms,睡眠时间2为2000ms,即unpark在park之后执行

    [t1] WARN c.test3 - park...
    [main] WARN c.test3 - unpark...
    [t1] WARN c.test3 - resume...
    

    运行结果2(设置睡眠时间1为2000ms,睡眠时间2为1000ms,即park在unpark之后执行

    [main] WARN c.test3 - unpark...
    [t1] WARN c.test3 - park...
    [t1] WARN c.test3 - resume...
    

    总结:上面的2个结果说明,park与unpark成对使用时,对使用前后的次序并不敏感。原因见原理部分。

    1-2 park/unpark与wait/notify的区别

    • wait/notify必须在有锁的情况下使用(需要关联Monitor对象),park/unpark没有这个限制条件。
    • park/unpark配对使用能够精确的指定具体的线程的阻塞/运行,notify只能随机唤醒一个线程
    • park/unpark配对使用可以先unpark,wait/notify配合使用不能够先notify

    1-3 park/unpark的底层原理

    1-3-1 先park后unpark的场景分析

    step1: 线程0在执行的过程中调用park方法。

    step2:检查_counter是否为0

    • 为0,获得_mutex互斥锁
    • 为1,说明之前其他线程调用过park方法,则将_counter设为1后线程继续执行。(先unpark后park的场景

    step3:获得互斥锁之后,线程进入 _cond 条件变量阻塞

    step4: 某线程在执行的过程中调用unpark方法后,设置_counter为1。

    step5:唤醒 _cond 条件变量中的 Thread_0

    step6:Thread_0 恢复运行 ,并恢复_counter为0。

    1-3-2 先unpark后park的场景分析

    step1: 某线程调用unpark方法后,_counter被设置为1。

    step2:线程0执行过程中调用park方法,检查_counter是1,无法获得互斥变量__mutex进入阻塞队列

    step3:线程0恢复_counter为0并继续执行。

    总结

    从2个例子中可以看到,park方法调用后,必须满足_counter为0,才能进入阻塞队列。如果在park之间调用unpark,那么park方法就会失效,无法让线程停止运行。

    2 JAVA中API层面的线程10种状态转换

    2-1 六种状态回顾

    NEW:线程刚刚被创建时,还没有start()的状态

    RUNABLE: Java中的RUNABLE包含了操作系统层面的运行,阻塞,可运行状态。

    • 操作系统层面的线程的运行,阻塞等在Java层面无法体现出来

    BLOCKED,WAITING,TIMED_WAITINGJava API层面的阻塞

    • TIMED_WAITING:使用sleep方法可能会出现
    • WAITING: 使用join方法后可能会出现
    • BLOCKED:使用synchronize方法可能会出现

    2-2 状态的转换10种情况分析

    假设有线程t。

    情况1 NEW --> RUNNABLE

    • 线程t被定义后,其状态为NEW,当调用 t.start() 方法时,由 NEW --> RUNNABLE

    情况2,3,4 RUNNABLE <--> WAITING

    wait/notify|wait/interrupt
    • 线程t何时从RUNNABLE变为WAITING?
      • t在执行过程中通过synchroized(obj)获取到锁(拥有了monitor的owner),然后调用obj.wait()方法,则线程进入WAITING状态(进入到monitor的waitset等待)。
    • 线程t何时从WAITING变为RUNABLE?
      • 调用 obj.notify() , obj.notifyAll() , t.interrupt() 时(notify或者使用中断)。
        • 竞争锁成功,t 线程从 WAITING --> RUNNABLE
        • 竞争锁失败,t 线程从 WAITING --> BLOCKED (进入到monitor的entryList等待)
    join|join/interrupt(可以将“当前线程”作为“主线程”)
    • 当前线程何时从RUNNABLE变为WAITING?
      • 当前线程(比如主线程)需要等待t线程运行结束,调用join()的时候(此时调用线程会进入到线程t关联的monitor中的waitset进行等待)。
    • 当前线程何时从WAITING变为RUNABLE?
      • t 线程运行结束 (猜测:线程t运行结束,其作为锁对象关联的monitor被回收,在monitor上等待的当前线程不会再等待又继续执行)
      • t线程调用当前线程的interrupt方法
    park/unpark| park/interrupt(可以将当前线程作为主线程)
    • 当前线程何时从RUNNABLE变为WAITING?

      • 当前线程调用 LockSupport.park() 方法
    • 当前线程何时从WAITING变为RUNABLE?‘

      • t线程调用了LockSupport.unpark(目标线程)
      • t线程调用了当前线程的interrupt方法

    情况5,6,7,8 RUNNABLE <--> TIMED_WAITING(结合情况2,3,4看)

    wait(n)/notify|wait(n)调用/等待足够时间|wait(n)/interrupt
    join(n)/interrupt|join(n)调用后/等待足够时间
    sleep(n)/等待足够时间
    parkNanos(nanos),parkUntil(millis) /unpark(目标线程) 或者interrupt或者等待超时

    情况9 RUNNABLE <--> BLOCKED

    • t线程何时从 RUNNABLE变为BLOCKED?

      • t 线程用 synchronized(obj) 尝试获取对象锁时但是竞争失败 (此时线程t会进入对象头的阻塞队列
    • 当前线程何时从BLOCKED变为RUNNABLE?‘

      • 持 obj 锁线程的同步代码块执行完毕,会唤醒该对象上所有 BLOCKED 的线程重新竞争,其中 t 线程竞争成功。

    情况 10 RUNNABLE <--> TERMINATED

    当前线程所有代码运行完毕,进入 TERMINATED


    2-3 WAITING与BLOCKED的区别(从图中分辨)

    总结:从上面的图可以看出WAITING线程与BLOCKED线程在不同地方。WAITING线程在对象头的WaitSet,而BLOCKED线程在对象头的EntryList

    • waiting:主动为之,wait()方法释放cpu执行权和释放锁进入对象头的Waitset ,需要notify()唤醒,然后进入到EntryList等待竞争锁。

    • blocked:被动的,线程在竞争锁的时候失败,被阻塞,进入EntryList。


    参考资料

    并发编程课程

    java线程中timed和blocked两种状态的区别


    20210303

  • 相关阅读:
    MySql
    Zookeeper
    Kafka
    Mybatis
    Spring/Spring MVC
    Spring Boot/Spring Cloud
    网络
    设计模式
    Strassen algorithm(O(n^lg7))
    dynamic programming:find max subarray
  • 原文地址:https://www.cnblogs.com/kfcuj/p/14476937.html
Copyright © 2011-2022 走看看