精华内容
下载资源
问答
  • promise._then(on_resolve_or_reject, on_resolve_or_reject) File "C:\Python38\lib\site-packages\promise\promise.py", line 577, in _then target._add_callbacks(did_fulfill, did_reject, promise)...
  • <div><p>I'm writing a test suite that attempts to wait for a reader to be ready (listening on ...s another event I should be waiting on?</p><p>该提问来源于开源项目:dudleycarr/nsqjs</p></div>
  • jstack dump的线程有一半的线程都在waiting on condition,而且都是一些tomcat的线程池之类的问题线程栈如下: ![图片说明](https://img-ask.csdn.net/upload/201904/19/1555638646_167074.png) 53%的线程都是...
  •  * * For example, a thread that has called {@code Object.wait()} * on an object is waiting for another thread to call * {@code Object.notify()} or {@code Object.notifyAll()...
    c0a392121d7c6c62319e19dcae88bc96.png

    前提

    最近有点懒散,没什么比较有深度的产出。刚好想重新研读一下JUC线程池的源码实现,在此之前先深入了解一下Java中的线程实现,包括线程的生命周期、状态切换以及线程的上下文切换等等。编写本文的时候,使用的JDK版本是11。

    Java线程的实现

    「JDK1.2之后」,Java线程模型已经确定了基于操作系统原生线程模型实现。因此,目前或者今后的JDK版本中,操作系统支持怎么样的线程模型,在很大程度上决定了Java虚拟机的线程如何映射,这一点在不同的平台上没有办法达成一致,虚拟机规范中也未限定Java线程需要使用哪种线程模型来实现。线程模型只对线程的并发规模和操作成本产生影响,对于Java程序来说,这些差异是透明的。

    对应Oracle Sun JDK或者说Oracle Sun JVM而言,它的Windows版本和Linux版本都是使用「一对一的线程模型」实现的(如下图所示)。

    51138f6f3eb24f29351caa2685111aee.png

    也就是一条Java线程就映射到一条轻量级进程(「Light Weight Process」)中,而一条轻量级线程又映射到一条内核线程(「Kernel-Level Thread」)。我们平时所说的线程,往往就是指轻量级进程(或者通俗来说我们平时新建的java.lang.Thread就是轻量级进程实例的一个"句柄",因为一个java.lang.Thread实例会对应JVM里面的一个JavaThread实例,而JVM里面的JavaThread就应该理解为轻量级进程)。前面推算这个线程映射关系,可以知道,我们在应用程序中创建或者操作的java.lang.Thread实例最终会映射到系统的内核线程,如果我们恶意或者实验性无限创建java.lang.Thread实例,最终会影响系统的正常运行甚至导致系统崩溃(可以在Windows开发环境中做实验,确保内存足够的情况下使用死循环创建和运行java.lang.Thread实例)。

    线程调度方式包括两种,协同式线程调度和抢占式线程调度。

    线程调度方式描述劣势优势协同式线程调度线程的执行时间由线程本身控制,执行完毕后主动通知操作系统切换到另一个线程上某个线程如果不让出CPU执行时间可能会导致整个系统崩溃实现简单,没有线程同步的问题抢占式线程调度每个线程由操作系统来分配执行时间,线程的切换不由线程自身决定实现相对复杂,操作系统需要控制线程同步和切换不会出现一个线程阻塞导致系统崩溃的问题

    Java线程最终会映射为系统内核原生线程,所以Java线程调度最终取决于系操作系统,而目前主流的操作系统内核线程调度基本都是使用抢占式线程调度。也就是可以死记硬背一下:「Java线程是使用抢占式线程调度方式进行线程调度的」

    很多操作系统都提供线程优先级的概念,但是由于平台特性的问题,Java中的线程优先级和不同平台中系统线程优先级并不匹配,所以Java线程优先级可以仅仅理解为“「建议优先级」”,通俗来说就是java.lang.Thread#setPriority(int newPriority)并不一定生效,「有可能Java线程的优先级会被系统自行改变」

    Java线程的状态切换

    Java线程的状态可以从java.lang.Thread的内部枚举类java.lang.Thread$State得知:

    public enum State {          NEW,    RUNNABLE,    BLOCKED,    WAITING,    TIMED_WAITING,    TERMINATED;}

    这些状态的描述总结成图如下:

    7f4229a2eccd457b56caaefc898a5d84.png

    「线程状态之间关系切换」图如下:

    7d4e89071421deca8ad9b018f2984338.png

    下面通过API注释和一些简单的代码例子分析一下Java线程的状态含义和状态切换。

    NEW状态

    「API注释」

    /** * Thread state for a thread which has not yet started. * */NEW,

    线程实例尚未启动时候的线程状态。

    一个刚创建而尚未启动(尚未调用Thread#start()方法)的Java线程实例的就是处于NEW状态。

    public class ThreadState {    public static void main(String[] args) throws Exception {        Thread thread = new Thread();        System.out.println(thread.getState());    }}// 输出结果NEW

    RUNNABLE状态

    「API注释」

    /** * Thread state for a runnable thread.  A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */RUNNABLE,

    可运行状态下线程的线程状态。可运行状态下的线程在Java虚拟机中执行,但它可能执行等待操作系统的其他资源,例如处理器。

    当Java线程实例调用了Thread#start()之后,就会进入RUNNABLE状态。RUNNABLE状态可以认为包含两个子状态:READY和RUNNING。

    • READY:该状态的线程可以被线程调度器进行调度使之更变为RUNNING状态。
    • RUNNING:该状态表示线程正在运行,线程对象的run()方法中的代码所对应的的指令正在被CPU执行。

    当Java线程实例Thread#yield()方法被调用时或者由于线程调度器的调度,线程实例的状态有可能由RUNNING转变为READY,但是从线程状态Thread#getState()获取到的状态依然是RUNNABLE。例如:

    public class ThreadState1 {    public static void main(String[] args) throws Exception {        Thread thread = new Thread(()-> {            while (true){                Thread.yield();            }        });        thread.start();        Thread.sleep(2000);        System.out.println(thread.getState());    }}// 输出结果RUNNABLE

    WAITING状态

    「API注释」

       /**    * Thread state for a waiting thread.    * A thread is in the waiting state due to calling one of the    * following methods:    * 
        *   {@link Object#wait() Object.wait} with no timeout    *   {@link #join() Thread.join} with no timeout    *   {@link LockSupport#park() LockSupport.park}    *     *    * 

    A thread in the waiting state is waiting for another thread to    * perform a particular action.    *    * For example, a thread that has called {@code Object.wait()}    * on an object is waiting for another thread to call    * {@code Object.notify()} or {@code Object.notifyAll()} on    * that object. A thread that has called {@code Thread.join()}    * is waiting for a specified thread to terminate.    */    WAITING,

    等待中线程的状态。一个线程进入等待状态是由于调用了下面方法之一:不带超时的Object#wait() 不带超时的Thread#join() LockSupport.park() 一个处于等待状态的线程总是在等待另一个线程进行一些特殊的处理。例如:一个线程调用了Object#wait(),那么它在等待另一个线程调用对象上的Object#notify()或者Object#notifyAll();一个线程调用了Thread#join(),那么它在等待另一个线程终结。

    WAITING是「无限期的等待状态」,这种状态下的线程不会被分配CPU执行时间。当一个线程执行了某些方法之后就会进入无限期等待状态,直到被显式唤醒,被唤醒后,线程状态由WAITING更变为RUNNABLE然后继续执行。

    RUNNABLE转换为WAITING的方法(无限期等待)WAITING转换为RUNNABLE的方法(唤醒)Object#wait()Object#notify() | Object#notifyAll()Thread#join()-LockSupport.part()LockSupport.unpart(thread)

    其中Thread#join()方法相对比较特殊,它会阻塞线程实例直到线程实例执行完毕,可以观察它的源码如下:

    public final void join() throws InterruptedException {    join(0);}public final synchronized void join(long millis)throws InterruptedException {    long base = System.currentTimeMillis();    long now = 0;    if (millis 

    可见Thread#join()是在线程实例存活的时候总是调用Object#wait()方法,也就是必须在线程执行完毕isAlive()为false(意味着线程生命周期已经终结)的时候才会解除阻塞。

    基于WAITING状态举个例子:

    public class ThreadState3 {    public static void main(String[] args) throws Exception {        Thread thread = new Thread(()-> {            LockSupport.park();            while (true){                Thread.yield();            }        });        thread.start();        Thread.sleep(50);        System.out.println(thread.getState());        LockSupport.unpark(thread);        Thread.sleep(50);        System.out.println(thread.getState());    }}// 输出结果WAITINGRUNNABLE

    TIMED WAITING状态

    「API注释」

    /*** Thread state for a waiting thread with a specified waiting time.* A thread is in the timed waiting state due to calling one of* the following methods with a specified positive waiting time:* 
    *   {@link #sleep Thread.sleep}*   {@link Object#wait(long) Object.wait} with timeout*   {@link #join(long) Thread.join} with timeout*   {@link LockSupport#parkNanos LockSupport.parkNanos}*   {@link LockSupport#parkUntil LockSupport.parkUntil}* */TIMED_WAITING,

    定义了具体等待时间的等待中线程的状态。一个线程进入该状态是由于指定了具体的超时期限调用了下面方法之一:Thread.sleep() 带超时的Object#wait() 带超时的Thread#join() LockSupport.parkNanos() LockSupport.parkUntil()

    TIMED WAITING就是「有限期等待状态」,它和WAITING有点相似,这种状态下的线程不会被分配CPU执行时间,不过这种状态下的线程不需要被显式唤醒,只需要等待超时限期到达就会被VM唤醒,有点类似于现实生活中的闹钟。

    RUNNABLE转换为TIMED WAITING的方法(有限期等待)TIMED WAITING转换为RUNNABLE的方法(超时解除等待)Object#wait(timeout)-Thread#sleep(timeout)-Thread#join(timeout)-LockSupport.parkNanos(timeout)-LockSupport.parkUntil(timeout)-

    举个例子:

    public class ThreadState4 {    public static void main(String[] args) throws Exception {        Thread thread = new Thread(()-> {            try {                Thread.sleep(1000);            } catch (InterruptedException e) {                //ignore            }        });        thread.start();        Thread.sleep(50);        System.out.println(thread.getState());        Thread.sleep(1000);        System.out.println(thread.getState());    }}// 输出结果TIMED_WAITINGTERMINATED

    BLOCKED状态

    「API注释」

    /*** Thread state for a thread blocked waiting for a monitor lock.* A thread in the blocked state is waiting for a monitor lock* to enter a synchronized block/method or* reenter a synchronized block/method after calling* {@link Object#wait() Object.wait}.*/BLOCKED,

    此状态表示一个线程正在阻塞等待获取一个监视器锁。如果线程处于阻塞状态,说明线程等待进入同步代码块或者同步方法的监视器锁或者在调用了Object#wait()之后重入同步代码块或者同步方法。

    BLOCKED状态也就是阻塞状态,该状态下的线程不会被分配CPU执行时间。线程的状态为BLOCKED的时候有两种可能的情况:

    A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method

    1. 线程正在等待一个监视器锁,只有获取监视器锁之后才能进入synchronized代码块或者synchronized方法,在此等待获取锁的过程线程都处于阻塞状态。

    reenter a synchronized block/method after calling Object#wait()

    1. 线程X步入synchronized代码块或者synchronized方法后(此时已经释放监视器锁)调用Object#wait()方法之后进行阻塞,当接收其他线程T调用该锁对象Object#notify()/notifyAll(),但是线程T尚未退出它所在的synchronized代码块或者synchronized方法,那么线程X依然处于阻塞状态(注意API注释中的「reenter」,理解它场景2就豁然开朗)。

    更加详细的描述可以参考笔者之前写过的一篇文章:深入理解Object提供的阻塞和唤醒API

    针对上面的场景1举个简单的例子:

    public class ThreadState6 {    private static final Object MONITOR = new Object();    public static void main(String[] args) throws Exception {        Thread thread1 = new Thread(()-> {            synchronized (MONITOR){                try {                    Thread.sleep(Integer.MAX_VALUE);                } catch (InterruptedException e) {                    //ignore                }            }        });        Thread thread2 = new Thread(()-> {            synchronized (MONITOR){                System.out.println("thread2 got monitor lock...");            }        });        thread1.start();        Thread.sleep(50);        thread2.start();        Thread.sleep(50);        System.out.println(thread2.getState());    }}// 输出结果BLOCKED

    针对上面的场景2举个简单的例子:

    public class ThreadState7 {    private static final Object MONITOR = new Object();    private static final DateTimeFormatter F = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");    public static void main(String[] args) throws Exception {        System.out.println(String.format("[%s]-begin...", F.format(LocalDateTime.now())));        Thread thread1 = new Thread(() -> {            synchronized (MONITOR) {                System.out.println(String.format("[%s]-thread1 got monitor lock...", F.format(LocalDateTime.now())));                try {                    Thread.sleep(1000);                    MONITOR.wait();                } catch (InterruptedException e) {                    //ignore                }                System.out.println(String.format("[%s]-thread1 exit waiting...", F.format(LocalDateTime.now())));            }        });        Thread thread2 = new Thread(() -> {            synchronized (MONITOR) {                System.out.println(String.format("[%s]-thread2 got monitor lock...", F.format(LocalDateTime.now())));                try {                    MONITOR.notify();                    Thread.sleep(2000);                } catch (InterruptedException e) {                    //ignore                }                System.out.println(String.format("[%s]-thread2 releases monitor lock...", F.format(LocalDateTime.now())));            }        });        thread1.start();        thread2.start();        // 这里故意让主线程sleep 1500毫秒从而让thread2调用了Object#notify()并且尚未退出同步代码块,确保thread1调用了Object#wait()        Thread.sleep(1500);          System.out.println(thread1.getState());        System.out.println(String.format("[%s]-end...", F.format(LocalDateTime.now())));    }}// 某个时刻的输出如下:[2019-06-20 00:30:22]-begin...[2019-06-20 00:30:22]-thread1 got monitor lock...[2019-06-20 00:30:23]-thread2 got monitor lock...BLOCKED[2019-06-20 00:30:23]-end...[2019-06-20 00:30:25]-thread2 releases monitor lock...[2019-06-20 00:30:25]-thread1 exit waiting...

    场景2中:

    • 线程2调用Object#notify()后睡眠2000毫秒再退出同步代码块,释放监视器锁。
    • 线程1只睡眠了1000毫秒就调用了Object#wait(),此时它已经释放了监视器锁,所以线程2成功进入同步块,线程1处于API注释中所述的reenter a synchronized block/method的状态。
    • 主线程睡眠1500毫秒刚好可以命中线程1处于reenter状态并且打印其线程状态,刚好就是BLOCKED状态。

    这三点看起来有点绕,多看几次多思考一下应该就能理解。

    TERMINATED状态

    「API注释」

    /** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED;

    终结的线程对应的线程状态,此时线程已经执行完毕。

    TERMINATED状态表示线程已经终结。一个线程实例只能被启动一次,准确来说,只会调用一次Thread#run()方法,Thread#run()方法执行结束之后,线程状态就会更变为TERMINATED,意味着线程的生命周期已经结束。

    举个简单的例子:

    public class ThreadState8 {    public static void main(String[] args) throws Exception {        Thread thread = new Thread(() -> {        });        thread.start();        Thread.sleep(50);        System.out.println(thread.getState());    }}// 输出结果TERMINATED

    上下文切换

    多线程环境中,当一个线程的状态由RUNNABLE转换为非RUNNABLE(BLOCKED、WAITING或者TIMED_WAITING)时,相应线程的上下文信息(也就是常说的Context,包括CPU的寄存器和程序计数器在某一时间点的内容等等)需要被保存,以便线程稍后恢复为RUNNABLE状态时能够在之前的执行进度的基础上继续执行。而一个线程的状态由非RUNNABLE状态进入RUNNABLE状态时可能涉及恢复之前保存的线程上下文信息并且在此基础上继续执行。这里的对「线程的上下文信息进行保存和恢复的过程」就称为上下文切换(Context Switch)。

    线程的上下文切换会带来额外的性能开销,这包括保存和恢复线程上下文信息的开销、对线程进行调度的CPU时间开销以及CPU缓存内容失效的开销(线程所执行的代码从CPU缓存中访问其所需要的变量值要比从主内存(RAM)中访问响应的变量值要快得多,但是「线程上下文切换会导致相关线程所访问的CPU缓存内容失效,一般是CPU的L1 Cache和L2 Cache」,使得相关线程稍后被重新调度到运行时其不得不再次访问主内存中的变量以重新创建CPU缓存内容)。

    在Linux系统中,可以通过vmstat命令来查看全局的上下文切换的次数,例如:

    $ vmstat 1

    对于Java程序的运行,在Linux系统中也可以通过perf命令进行监视,例如:

    $ perf stat -e cpu-clock,task-clock,cs,cache-reference,cache-misses java YourJavaClass

    参考资料中提到Windows系统下可以通过自带的工具perfmon(其实也就是任务管理器)来监视线程的上下文切换,实际上笔者并没有从任务管理器发现有任何办法查看上下文切换,通过搜索之后发现了一个工具:Process Explorer。运行Process Explorer同时运行一个Java程序并且查看其状态:

    7408a402287f1f1aaed1c31267560710.png

    因为打了断点,可以看到运行中的程序的上下文切换一共7000多次,当前一秒的上下文切换增量为26(因为笔者设置了Process Explorer每秒刷新一次数据)。

    监控线程状态

    如果项目在生产环境中运行,不可能频繁调用Thread#getState()方法去监测线程的状态变化。JDK本身提供了一些监控线程状态的工具,还有一些开源的轻量级工具如阿里的Arthas,这里简单介绍一下。

    使用jvisualvm

    jvisualvm是JDK自带的堆、线程等待JVM指标监控工具,适合使用于开发和测试环境。它位于JAVA_HOME/bin目录之下。

    208f033275a192fe13b7e2dbca34e3c1.png

    其中线程Dump的按钮类似于下面要提到的jstack命令,用于导出所有线程的栈信息。

    使用jstack

    jstack是JDK自带的命令行工具,功能是用于获取指定PID的Java进程的线程栈信息。例如本地运行的一个IDEA实例的PID是11376,那么只需要输入:

    jstack 11376

    然后控制台输出如下:

    fcc4a8854ed8fafc36f5b07777b17e22.png

    另外,如果想要定位具体Java进程的PID,可以使用jps命令。

    使用JMC

    JMC也就是Java Mission Control,它也是JDK自带的工具,提供的功能要比jvisualvm强大,包括MBean的处理、线程栈已经状态查看、飞行记录器等等。

    001c77ab44472fede30767870edf0c5c.png

    小结

    理解Java线程状态的切换和一些监控手段,更有利于日常开发多线程程序,对于生产环境出现问题,通过监控线程的栈信息能够快速定位到问题的根本原因(通常来说,目前比较主流的MVC应用(准确来说应该是Servlet容器如Tomcat)都是通过一个线程处理一个单独的请求,当请求出现阻塞的时候,导出对应处理请求的线程基本可以定位到阻塞的精准位置,如果使用消息队列例如RabbitMQ,消费者线程出现阻塞也可以利用相似的思路解决)。

    展开全文
  • 所谓condition条件变量,即这种机制是在满足了特定的条件后,线程才可以访问相关的数据。 这种同步机制就是一个线程等待特定的条件,另一个线程通知它条件已经发生。一旦条件发生,该线程就会获取锁,从而独占共享...
    b8ef543b1d1b4f9c438b2561be4e9c05.png

    什么是Condtion?

    所谓condition条件变量,即这种机制是在满足了特定的条件后,线程才可以访问相关的数据。 这种同步机制就是一个线程等待特定的条件,另一个线程通知它条件已经发生。一旦条件发生,该线程就会获取锁,从而独占共享资源的访问。

    Condition包含以下部分:

    • c.acquire(*args):获取底层锁。此方法将调用底层锁上对应的acquire(*args)方法。
    • c.release():释放底层锁。此方法将调用底层锁上对应的release()方法
    • c.wait(timeout):等待直到获取通知或出现超时为止。此方法在调用线程已经获取锁之后调用。
    • 调用时,将释放底层锁,而且线程将进入睡眠状态,直到另一个线程在条件变量上执行notify()或notify_all()方法将其唤醒为止。在线程被唤醒后,线程讲重新获取锁,方法也会返回。timeout是浮点数,单位为秒。如果超时,线程将被唤醒,重新获取锁,而控制将被返回。
    • c.notify(n):唤醒一个或多个等待此条件变量的线程。此方法只会在调用线程已经获取锁之后调用,
    • 而且如果没有正在等待的线程,它就什么也不做。
    • n指定要唤醒的线程数量,默认为1.被唤醒的线程在它们重新获取锁之前不会从wait()调用返回。
    • c.notify_all():唤醒所有等待此条件的线程。

    通俗的解释:

    Python提供的Condition对象提供了对复杂线程同步问题的支持。Condition被称为条件变量,除了提供与Lock类似的 acquire和release方法外,还提供了wait和notify方法。线程首先acquire一个条件变量,然后判断一些条件。如果条件不满足则 wait;如果条件满足,进行一些处理改变条件后,通过notify方法通知其他线程,其他处于wait状态的线程接到通知后会重新判断条件。不断的重复 这一过程,从而解决复杂的同步问题。

    可以认为Condition对象维护了一个锁(Lock/RLock)和一个waiting池。线程通过acquire获得Condition对 象,当调用wait方法时,线程会释放Condition内部的锁并进入blocked状态,同时在waiting池中记录这个线程。当调用notify 方法时,Condition对象会从waiting池中挑选一个线程,通知其调用acquire方法尝试取到锁。

    Condition对象的构造函数可以接受一个Lock/RLock对象作为参数,如果没有指定,则Condition对象会在内部自行创建一个RLock。

    带有缓冲区的生产者-消费者模型

    我们可以根据所谓的wait池构建一个带有缓冲区的生产者-消费者模型,即缓冲区好比一个仓库,生产者可以不断生产商品知道仓库装满,然后告知消费者消费,而消费者也可以判断仓库是否满了从而告知生产者继续生产商品:

    import threadingimport time# 假设商品数量goods = 0condition = threading.Condition()def consumer(): global goods while True: condition.acquire() if goods <= 0: # 仓库空了,即特定条件满足了,通知生产者生产 condition.notify() condition.wait() time.sleep(2) goods -= 1 print('consume 1, left {}'.format(goods)) time.sleep(2) condition.release()def producer(): global goods while True: condition.acquire() if goods >= 5: # 仓库满了,即特定条件满足了,通知消费者消费 condition.notify() condition.wait() time.sleep(2) goods += 1 print('produce 1, already {}'.format(goods)) time.sleep(2) condition.release()if __name__ == '__main__': thread_consumer = threading.Thread(target=consumer) thread_producer = threading.Thread(target=producer) thread_consumer.start() thread_producer.start() thread_consumer.join() thread_producer.join() print('consumer-producer example end.')

    运行截图如下:

    5347f83034bea4532ef7c55897a35d7a.png

    运行结果

    展开全文
  • 最近发现个问题,不知道是使用不当还是何种...现象,运行一段时间后,Quartz的线程池中9个线程的状态是Waiting on Condition,一个是runnable。整进程的状态貌似停住了,调度时间到了,没有执行高度线程中的代码。...

    最近发现个问题,不知道是使用不当还是何种原因造成的。

    具体内容:

         用Quartz调度任务,周期10分钟。但具体业务执行的时间要大于10分钟。Quartz线程池的配置是缺省,查了一下10个大小。

    现象,运行一段时间后,Quartz的线程池中9个线程的状态是Waiting on Condition,一个是runnable。整进程的状态貌似停住了,调度时间到了,没有执行高度线程中的代码。

       

         怀疑,由于业务操作的时候大于10分钟(调度周期),可能会导致(猜的)Quartz的线程池中的线程还在处理业务,一次调度结束但实际业务操作还在运行(指在调度的线程池中运行),运行一段时间后这样的线程变多,导致无法响应下一次调度。

     

        解决办法,目前设想的是,具体的业务,在另一个线程池中完成而不使用Quartz的线程池做具体业务操作,每一次调度线程只把任务放到具体执行线程池中,就结束。

     

        继续上面说的,把一个实际基于FTP的文件定时扫描发送程序按上述思路调整了下。

     

    调整前:

     

    调度执行内容:

    1.扫述表找到要转发的文件到LIST

    2.遍历LIST,顺序发送文件。

     

    调整后:

     

    调度执行内容:

    1.扫述表找到要转发的文件到LIST

    2.遍历LIST,以每个元素构造转发Thread并提交到线程池。(或者设个最大转发数来控制同时转发的数量)

     

    最后的结论不知道是否正确,业务操作时间较长的话,不要在调试本身的线程池中做。

    展开全文
  • (22)对于重入锁,如果内部调用了condition.await(timeout)之后且被signal()或超时了的,状态会有一个从TIMED_WAITING切换到WAITING的过程,也就是从等待队列进入到同步队列; 为了便于理解,彤哥这里每一条都分的...
    c5ad01e54f510e2d55e06cb09a254dc1.png

    (手机横屏看源码更方便)

    注:java源码分析部分如无特殊说明均基于 java8 版本。

    简介

    大家都知道线程是有生命周期,但是彤哥可以认真负责地告诉你网上几乎没有一篇文章讲得是完全正确的。

    常见的错误有:就绪状态、运行中状态(RUNNING)、死亡状态、中断状态、只有阻塞没有等待状态、流程图乱画等,最常见的错误就是说线程只有5种状态。

    今天这篇文章会彻底讲清楚线程的生命周期,并分析synchronized锁、基于AQS的锁中线程状态变化的逻辑。

    所以,对synchronized锁和AQS原理(源码)不了解的同学,请翻一下彤哥之前的文章先熟悉这两部分的内容,否则肯定记不住这里讲的线程生命周期。

    问题

    (1)线程的状态有哪些?

    (2)synchronized锁各阶段线程处于什么状态?

    (3)重入锁、条件锁各阶段线程处于什么状态?

    先上源码

    关于线程的生命周期,我们可以看一下java.lang.Thread.State这个类,它是线程的内部枚举类,定义了线程的各种状态,并且注释也很清晰。

    public enum State { /** * 新建状态,线程还未开始 */ NEW, /** * 可运行状态,正在运行或者在等待系统资源,比如CPU */ RUNNABLE, /** * 阻塞状态,在等待一个监视器锁(也就是我们常说的synchronized) * 或者在调用了Object.wait()方法且被notify()之后也会进入BLOCKED状态 */ BLOCKED, /** * 等待状态,在调用了以下方法后进入此状态 * 1. Object.wait()无超时的方法后且未被notify()前,如果被notify()了会进入BLOCKED状态 * 2. Thread.join()无超时的方法后 * 3. LockSupport.park()无超时的方法后 */ WAITING, /** * 超时等待状态,在调用了以下方法后会进入超时等待状态 * 1. Thread.sleep()方法后【本文由公从号“彤哥读源码”原创】 * 2. Object.wait(timeout)方法后且未到超时时间前,如果达到超时了或被notify()了会进入BLOCKED状态 * 3. Thread.join(timeout)方法后 * 4. LockSupport.parkNanos(nanos)方法后 * 5. LockSupport.parkUntil(deadline)方法后 */ TIMED_WAITING, /** * 终止状态,线程已经执行完毕 */ TERMINATED;}

    流程图

    线程生命周期中各状态的注释完毕了,下面我们再来看看各状态之间的流转:

    5ff0e575688c7de15b9bf0b66fba3d37.png

    怎么样?是不是很复杂?彤哥几乎把网上的资料都查了一遍,没有一篇文章把这个流程图完整画出来的,下面彤哥就来一一解释:

    (1)为了方便讲解,我们把锁分成两大类,一类是synchronized锁,一类是基于AQS的锁(我们拿重入锁举例),也就是内部使用了LockSupport.park()/parkNanos()/parkUntil()几个方法的锁;

    (2)不管是synchronized锁还是基于AQS的锁,内部都是分成两个队列,一个是同步队列(AQS的队列),一个是等待队列(Condition的队列);

    (3)对于内部调用了object.wait()/wait(timeout)或者condition.await()/await(timeout)方法,线程都是先进入等待队列,被notify()/signal()或者超时后,才会进入同步队列;

    (4)明确声明,BLOCKED状态只有线程处于synchronized的同步队列的时候才会有这个状态,其它任何情况都跟这个状态无关;

    (5)对于synchronized,线程执行synchronized的时候,如果立即获得了锁(没有进入同步队列),线程处于RUNNABLE状态;

    (6)对于synchronized,线程执行synchronized的时候,如果无法获得锁(直接进入同步队列),线程处于BLOCKED状态;

    (5)对于synchronized内部,调用了object.wait()之后线程处于WAITING状态(进入等待队列);

    (6)对于synchronized内部,调用了object.wait(timeout)之后线程处于TIMED_WAITING状态(进入等待队列);

    (7)对于synchronized内部,调用了object.wait()之后且被notify()了,如果线程立即获得了锁(也就是没有进入同步队列),线程处于RUNNABLE状态;

    (8)对于synchronized内部,调用了object.wait(timeout)之后且被notify()了,如果线程立即获得了锁(也就是没有进入同步队列),线程处于RUNNABLE状态;

    (9)对于synchronized内部,调用了object.wait(timeout)之后且超时了,这时如果线程正好立即获得了锁(也就是没有进入同步队列),线程处于RUNNABLE状态;

    (10)对于synchronized内部,调用了object.wait()之后且被notify()了,如果线程无法获得锁(也就是进入了同步队列),线程处于BLOCKED状态;

    (11)对于synchronized内部,调用了object.wait(timeout)之后且被notify()了或者超时了,如果线程无法获得锁(也就是进入了同步队列),线程处于BLOCKED状态;

    (12)对于重入锁,线程执行lock.lock()的时候,如果立即获得了锁(没有进入同步队列),线程处于RUNNABLE状态;

    (13)对于重入锁,线程执行lock.lock()的时候,如果无法获得锁(直接进入同步队列),线程处于WAITING状态;

    (14)对于重入锁内部,调用了condition.await()之后线程处于WAITING状态(进入等待队列);

    (15)对于重入锁内部,调用了condition.await(timeout)之后线程处于TIMED_WAITING状态(进入等待队列);

    (16)对于重入锁内部,调用了condition.await()之后且被signal()了,如果线程立即获得了锁(也就是没有进入同步队列),线程处于RUNNABLE状态;

    (17)对于重入锁内部,调用了condition.await(timeout)之后且被signal()了,如果线程立即获得了锁(也就是没有进入同步队列),线程处于RUNNABLE状态;

    (18)对于重入锁内部,调用了condition.await(timeout)之后且超时了,这时如果线程正好立即获得了锁(也就是没有进入同步队列),线程处于RUNNABLE状态;

    (19)对于重入锁内部,调用了condition.await()之后且被signal()了,如果线程无法获得锁(也就是进入了同步队列),线程处于WAITING状态;

    (20)对于重入锁内部,调用了condition.await(timeout)之后且被signal()了或者超时了,如果线程无法获得锁(也就是进入了同步队列),线程处于WAITING状态;

    (21)对于重入锁,如果内部调用了condition.await()之后且被signal()之后依然无法获取锁的,其实经历了两次WAITING状态的切换,一次是在等待队列,一次是在同步队列;

    (22)对于重入锁,如果内部调用了condition.await(timeout)之后且被signal()或超时了的,状态会有一个从TIMED_WAITING切换到WAITING的过程,也就是从等待队列进入到同步队列;

    为了便于理解,彤哥这里每一条都分的比较细,麻烦耐心看完。

    测试用例

    看完上面的部分,你肯定想知道怎么去验证,下面彤哥就说说验证的方法,先给出测试用例。

    public class ThreadLifeTest { public static void main(String[] args) throws IOException { Object object = new Object(); ReentrantLock lock = new ReentrantLock(); Condition condition = lock.newCondition(); new Thread(()->{ synchronized (object) { try { System.out.println("thread1 waiting"); object.wait();// object.wait(5000); System.out.println("thread1 after waiting"); } catch (InterruptedException e) { e.printStackTrace(); } } }, "Thread1").start(); new Thread(()->{ synchronized (object) { try { System.out.println("thread2 notify"); // 打开或关闭这段注释,观察Thread1的状态// object.notify();【本文由公从号“彤哥读源码”原创】 // notify之后当前线程并不会释放锁,只是被notify的线程从等待队列进入同步队列 // sleep也不会释放锁 Thread.sleep(10000000); } catch (InterruptedException e) { e.printStackTrace(); } } }, "Thread2").start(); new Thread(()->{ lock.lock(); System.out.println("thread3 waiting"); try { condition.await();// condition.await(200, (TimeUnit).SECONDS); System.out.println("thread3 after waiting"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }, "Thread3").start(); new Thread(()->{ lock.lock(); System.out.println("thread4"); // 打开或关闭这段注释,观察Thread3的状态// condition.signal();【本文由公从号“彤哥读源码”原创】 // signal之后当前线程并不会释放锁,只是被signal的线程从等待队列进入同步队列 // sleep也不会释放锁 try { Thread.sleep(1000000); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } }, "Thread4").start(); }}

    打开或关闭上面注释部分的代码,使用IDEA的RUN模式运行代码,然后点击左边的一个摄像头按钮(jstack),查看各线程的状态。

    注:不要使用DEBUG模式,DEBUG模式全都变成WAITING状态了,很神奇。

    604e25f9fe1b5fe514aa9ec060f6756b.png

    彩蛋

    其实,本来这篇是准备写线程池的生命周期的,奈何线程的生命周期写了太多,等下一篇我们再来一起学习线程池的生命周期吧。

    展开全文
  • chaos-testing</code> I got <code>Error: failed post-install: timed out waiting for the condition</code>. <p><code>kubectl get pods --namespace chaos-testing -l app.kubernetes.io/instance=chaos-...
  • <p>it seems like every so often, something is interrupting the stream returned from docker while waiting on a <code>log</code> condition. the annoying things is, i have no real test case that can ...
  • <div><p>This is caused by the data loading late waiting for a static query that is in the preview and shouldn't be. <p><strong>What kind of change does this PR introduce?</strong></p> <p>A bugfix ...
  • failed to initialize the cluster: Some cluster operators are still updating: authentication, console: timed out waiting for the condition" <h1>What you expected to happen? <p>OCP Cluster is ...
  • <p><code>failed to save outputs: timed out waiting for the condition</code></p> <h3>What did you expect to happen: <ul><li>The pipeline to run successfully using either KFP or argo workflows. I ...
  • Calabash::Cucumber::WaitHelpers::WaitError: Timeout waiting () for condition (NONE_ANIMATING) from /usr/local/var/rbenv/versions/2.3.1/lib/ruby/gems/2.3.0/gems/calabash-cucumber-0.20.0/lib/calabash-...
  • <ul><li>fixes a race condition where the test writes to the dispatcher and closes the LogStream without waiting for the written event to have been persisted on disk</li><li>removes some unused methods...
  • Error: timed out waiting for the condition Error during 'cluster up' execution: Error starting the cluster. ssh command error: command : /var/lib/minishift/bin/oc cluster up --server-loglevel...
  • WaitForMultipleObjects returns a strangely cooked DWORD value that can have flags meaning error, time out, signaled or abandoned condition, as well as an index into a handle in the array that is pas
  • Waiting on futures hangs

    2020-11-28 16:36:45
    and the race condition doesn't occur), I see many thousands of these messages: <p><code>00000000000002cf] <warning> [TM] set_state: thread is currently active, scheduling new thread, thread...
  • <p>Implementation of the waiting part could be <strong>initially</strong> somewhere along the lines (from -wieser ) : <pre><code>python3 async def OnCondition(clk, signals, condition) while True: ...
  • error marking master: timed out waiting for the condition </code></pre> <p>But, all docker containers were work fine. <pre><code> [root kubeadm]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS ...
  • The only thing that is restrictive on my environment is that I cannot use DHCP. I have to use Static IP addresses everywhere. The ControlPlane VM is created, a static IP is given, I can ping the VM ...
  • failed to initialize the cluster: Some cluster operators are still updating: console, image-registry: timed out waiting for the condition </code></pre> <p>will become: <pre><code> failed to ...
  • <div><p>[kind/bug] ... Error: unable to wait for build nodejs-myapp-1 to run: timed out waiting for the condition [...] </nil></code></pre>该提问来源于开源项目:openshift/odo</p></div>
  • <div><p>Waiting on an empty token will bypass the allocated check because bitwise and won't fail. It will then cause an infinite loop because the condition will never fulfil. <p>Instead, waiting ...
  • 请教一下 JStack中的wait on condition是什么意思? 我发现,某个线程在jstack中被标识为“wait on condition”时, 下面的线程状态,有时候是:“RUNNABLE”,也有的时候是:“TIMED_WAITING” 至于其他状态,...
  • [custom-ca-certificates default-token-nsd89 gitaly-config init-gitaly-secrets gitaly-secrets repo-data etc-ssl-certs]: timed out waiting for the condition Warning FailedMount 4m6s kubelet, node00b....
  • Add an object head condition variable to wait on and use it for ESI Previously, ved_include() for ESI include processing retried the CNT_Request() FSM step if it hit a busy object, sleeping 10ms in...
  • 7301 INFO comms-gatt Waiting for device connection... 8423 INFO hub Attached peripheral: EncodedMotor on port 0x0 18323 WARNING hub Got only these devices: (EncodedMotor on port 0x0, None, None, None,...
  • failed to sync cache: timed out waiting for the condition" failed to sync cache: timed out waiting for the condition </code></pre> <p>on v0.7.3 I get: <pre><code> time="2020-10-20T06:25:...
  • And error message: Error: release www-crm failed: timed out waiting for the condition</code> </li><li>Second run (helm package is in FAILED state): <code>Command returned with exit code: 1. And ...
  • The command is on a parallel common event. <p>The failing condition is met once the magic bag is provided after recruiting 3 heroes in the bar. First, get the legendary sword, you will be the chosen ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,466
精华内容 586
关键字:

conditiononwaiting