Java线程系列(3)——线程的动作与状态的关系

本文探讨了Java线程的不同动作及其与线程状态的关系,包括runnable、inObject.wait()、waitingformonitorentry及waitingoncondition等动作,并通过实例说明了不同动作下线程的具体状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java线程系列(3)——线程的动作与状态的关系

前两篇文章分析了Java线程的六种状态以及每种状态的对应的 thread dump 的特性, 调用栈反应了线程执行的代码路径, 调用修饰反应出了线程执行过程中 等待锁/拥有锁/释放锁 的附加信息。线程的状态:

线程dump信息的第一行, 会有waiting on conditionwaiting for monitor entryin Object.wait()等信息, 它其实是线程的动作(即线程的行为)。线程的动作造成线程状态的改变, 也就是说线程的动作是线程状态产生的原因。

本文是Java线程系列文章的第三篇, 主要来讨论线程的动作, 以及具体的动作产生的线程状态。

1. 线程的动作

线程的动作和线程的状态并不是一一对应的关系, 即两个线程执行了同样的动作, 但是可以产生不同的状态, 这之间的差别主要由线程调用Java层面的API决定。线程的动作有如下几种:

  • runnable
  • in Object.wait()
  • waiting for monitor entry
  • waiting on condition
2. runnable

代表当前的线程正在执行, 或者它拿到了CPU的执行时间, 正准备执行。一般这种情况下线程的状态为RUNNABLE。但要注意, 是不是线程的状态为RUNNABLE时, 它一定在执行呢? 不一定, 因为Java线程的状态代表了线程在Java虚拟机中的执行状态, 并不能真实反映它在操作系统中的状态。RUNNABLE的官方解释为: 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.

3. waiting for monitor entry & in Object.wait()

waiting for monitor entryin Object.wait() 这两个动作和线程的同步与协作有关。在多线程的Java程序中,实现线程之间的同步协作, 其中一种非常常用的方案是Monitor。Monitor在
Java线程系列(1)——thread dump格式、锁与线程的状态 — Java Monitor
讨论过, 这里不再重复。

3.1 waiting for monitor entry

线程的动作为waiting for monitor entry, 代表当前线程正在对象锁的 Entry Set 区中竞争锁。如果竞争锁超时, 则线程的状态为BLOCKED (on object monitor), on object monitor 修饰了线程阻塞的原因, 同时印证了线程正在竞争锁。如果竞争锁成功, 则调用修饰中会有- locked修饰。

3.2 in Object.wait()

线程的动作为in Object.wait(), 代表线程释放锁进入锁的 Wait Set 区中等待特定的动作(其他线程进行唤醒或者超时等)。这时线程的状态是什么, 则由线程调用的 Object.wait 方法决定:

当调用 Object.wait() 时, 线程的状态为 WAITING, 即线程无期限等待其他线程的动作。例子如下:

package liyin.code;
public class App {
    public static void main(String[] args) {
        Runnable task = new Runnable() {
            public void run() {
                // App 类的字节码对象锁
                synchronized (App.class) {
                    try {
                        // 进入 App 类的字节码对象锁的 Wait Set
                        App.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        new Thread(task, "bh_001").start();
    }
}

"bh_001" prio=5 tid=0x00007fb6f687a800 nid=0x5403 in Object.wait() [0x00007000014d2000]
   java.lang.Thread.State: WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000007aab24250> (a java.lang.Class for liyin.code.App)
    at java.lang.Object.wait(Object.java:503)
    at liyin.code.App$1.run(App.java:13)
    - locked <0x00000007aab24250> (a java.lang.Class for liyin.code.App)
    at java.lang.Thread.run(Thread.java:745)

当调用 Object.wait(timeout) 时, 线程的状态为 TIMED_WAITING, 即线程有期限等待其他线程的动作。如果等待时间超过timeout, 则线程自行唤醒并参与到锁竞争中。例子如下:

package liyin.code;
public class App {
    public static void main(String[] args) {
        Runnable task = new Runnable() {
            public void run() {
                // App 类的字节码对象锁
                synchronized (App.class) {
                    try {
                        // 进入 App 类的字节码对象锁的 Wait Set
                        App.class.wait(20000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
        new Thread(task, "bh_002").start();
    }
}

"bh_002" prio=5 tid=0x00007fd35a85b800 nid=0x5503 in Object.wait() [0x0000700001555000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x00000007aab244a0> (a java.lang.Class for liyin.code.App)
    at liyin.code.App$1.run(App.java:13)
    - locked <0x00000007aab244a0> (a java.lang.Class for liyin.code.App)
    at java.lang.Thread.run(Thread.java:745)
4. waiting on condition

线程的动作是waiting on condition, 代表线程在等待某个条件的发生或者sleep。此时线程的状态一般是 TIMED_WAITING (parking)TIMED_WAITING (sleeping), 即有期限的挂起和睡眠状态。具体的例子请参看
Java线程系列(2)——线程有限等待状态分析 — Thread.sleep
Java线程系列(2)——线程有限等待状态分析 — JUC限时API

当线程为 waiting on condition 时,具体是什么原因,可以结合 stacktrace来分析。最常见的情况是线程在等待网络的读写,比如当网络数据没有准备好读时,线程处于这种等待状态,而一旦有数据准备好读之后,线程会重新激活,读取并处理数据。在Java引入 New IO之前,对于每个网络连接,都有一个对应的线程来处理网络的读写操作,即使没有可读写的数据,线程仍然阻塞在读写操作上,这样有可能造成资源浪费,而且给操作系统的线程调度也带来压力。在 New IO里采用了新的机制,编写的服务器程序的性能和可扩展性都得到提高。
如果发现有大量的线程都处在 Wait on condition,从线程 stack看, 正等待网络读写,这可能是一个网络瓶颈的征兆。因为网络阻塞导致线程无法执行。一种情况是网络非常忙,几乎消耗了所有的带宽,仍然有大量数据等待网络读写;另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达。所以要结合系统的一些性能观察工具来综合分析,比如 netstat统计单位时间的发送包的数目,看是否很明显超过了所在网络带宽的限制;观察cpu的利用率,看系统态的CPU时间是否明显大于用户态的CPU时间;如果程序运行在 Solaris 10平台上,可以用dtrace工具看系统调用的情况,如果观察到 read/write的系统调用的次数或者运行时间遥遥领先;这些都指向由于网络带宽所限导致的网络瓶颈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值