线程的“等待状态” 与 “阻塞状态”

众所周知,在Java中Thread 有六种状态,分别是 “新建状态”、“可运行状态”、“终止状态”、“阻塞状态”、“等待状态”、“限时等待状态”。本次讨论我们只关注"等待状态"、“限时等待状态”、“阻塞状态”的区别。本人知识储备比较浅,故本文只是对我已有的相关知识的一个小总结。

一、导致线程处于“等待状态”、“限时等待状态”的原因


(一)等待状态

        调用以下的方法会导致线程进入“等待状态”:

            1、不带超时值的Object.wait

            2、不带超时值的Thread.join

            3、LockSupport.park

    这些方法都有一个共同的特点,就是Thread需要等待某个条件被满足时,才会转变为“可运行状态”。

(二)限时等待状态

        调用以下的方法会导致线程进入“限时等待状态”:

            1、Thread.sleep

            2、带有超时值的 Object.wait

            3、带有超时值的 Thread.join

            4、LockSupport.parkNanos

            5、LockSupport.parkUntil

        这些方法都有一个共同的特点,就是Thread需要 等待某个条件被满足 等待超时 时,才会转变为“可运行状态”。

二、导致线程处于“阻塞状态”的原因


        等待同步代码块/方法时,未能获取到锁(即没有成为与锁对象关联的Monitor对象的所有者)。等另一个线程释放锁,并且当前线程获取到锁时,才会转变为“可运行状态”。

注:从上面的描述来看,处于“阻塞状态”的线程要想转变为“可运行状态”,好像也有一个等待状态(即获取到锁)。那么“等待状态”等待的条件 与 “阻塞状态”等待的条件有什么区别呢?首先前者等待的条件种类比较多,后者等待的条件只有一个(获取到锁)。故两者实现的机制也不同。

三、Monitor(锁)

        每个临界区都有一个用于获取锁的【锁对象】,如以下同步代码块中的【obj对象】。

                                                              synchronized(obj) \left\{ //临界区带的代码 \right\}

        首先会将synchronized中的锁对象的对象头的MarkWord字段去尝试指向操作系统提供的Monitor对象,让锁对象中的MarkWord字段Monitor对象相关联. 如果关联成功, 将obj对象头中的MarkWord字段值从01改为10。

锁对象、Monitor对象、Thread对象之间的关系

        【锁对象的对象头的MarkWord字段】 指向 【Monitor对象】,且该【Monitor对象】是被独享的,即【锁对象】【Monitor对象】之间是一一对应的。【Monitor对象】其实就是严格意义上的锁,而【锁对象】更像是锁的一个引用。【Monitor对象】的 【Owner字段 】记录着【持有锁的线程】,即锁的所有者。而在抢占锁的过程中,没有获得锁的线程会被保存在【 EntryList】 中,等待【持有锁的线程】释放锁时重新抢占锁。

            以Object.wait为例调用【锁对象】的【wait()方法】,会导致线程会释放持有的锁,同时线程对象被添加到【WaitSet】中,而被添加【WaitSet】中的该线程会 等待被其他线程唤醒 等待超 时。在【WaitSet】中的线程 被唤醒超时 后,会被转移到【EntryList】中,等待抢占锁。

拓展的内容:如何中断线程的阻塞?

      从广义上的“阻塞”来说,Java中有三种:

            1、线程处于"等待状态"、“限时等待状态”的阻塞。

            2、线程处于 "阻塞状态"的阻塞。

            3、I/O操作导致的阻塞(此时线程处于“可运行状态”)。

    对Java中的线程中断机制而言,只有第一种阻塞会响应中断请求。对于第二种阻塞而言,可以通过使用实现了Lock接口的锁来获得可中断的特性。对于第三种阻塞而言,可以通过关闭I/O流之类的方法引起I/O异常,进而达到中断I/O操作实现中断阻塞的目的。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容