关于线程状态之间的转换了不少东西,也看了一些源码,每次看都是一遍加深理解的过程。今天再理一遍。
先借用别人的一张图。(觉得有点不够全面,回头把自己的补上来。)
Enum Thread.State
补充说明之前先贴上Enum Thread.State
的定义。保留了源码注释,以方便查看。为什么要从这里说起?是因为我们平时说到线程状态的时候,总是会说阻塞,而有时候说的阻塞其实是等待,总是傻傻分不清楚。(或者是和操作系统中进程状态混在一起。)而通过jstack
去查看的时候看到的线程状态都是Tread.State.WAITING
等。因此还是有必要对照着真正的State来看,加深理解。
public enum State {
/**
* Thread state for a thread which has not yet started.
*/
NEW,
/**
* 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,
/**
* 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,
/**
* 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 Object.wait()
* on an object is waiting for another thread to call
* Object.notify() or Object.notifyAll() on
* that object. A thread that has called Thread.join()
* is waiting for a specified thread to terminate.
*/
WAITING,
/**
* 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 state for a terminated thread.
* The thread has completed execution.
*/
TERMINATED;
}
线程状态转换
接下来解释下文中最开始的图。
- 新建(NEW):新创建了一个线程对象。(三种方式:继承
Thread
; 实现Runnable
;实现Callable
) - 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的
start()
方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取CPU的使用权。运行中(RUNNING)的线程在时间片用完,或者主动调用yield()
放弃当前时间片的情况下也会转入可运行(RUNNABLE)状态。 - 运行中(注意State中是没有这么一个状态的,我们权且称这个动态的状态为RUNNING):可运行状态(RUNNABLE)的线程获得了CPU时间片(timeslice),执行程序代码。
- 等待(WAITING):从运行中到等待的几种可能性:
- 等待用户输入
Thread.sleep
-
LockSupport.park
参考JAVA并发梳理(一)LockSupport -
t2.join()
原理其实上,在t2线程对象上调用了wait
方法。且join(int)
是Synchronized
。这也呼应了两点:(1)调用wait
和notify
的时候一定是要在得到了对象锁的前提下; (2)wait
的时候会释放锁,否则别的线程怎么拿到锁来notify
呢:)。看源码:
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 < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
while (isAlive()) {
wait(0);
}
} else {
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break;
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}
4I. 限时等待(TIMED_WAITING)同上,只不过加了时限。
- 阻塞(BLOCKED):运行(RUNNING)的线程在获取对象的同步锁时,若该同步锁 被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。经历过
wait
,被notify
之后的线程也要进入阻塞状态重新请求锁。 - 终止(TERMINATED):线程run()、main()方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。
补充:
- 如果
wait(int) timeout
之后,没有拿到锁之前,会进入BLOCKED状态。 - 在同步块中,
notify()
之后,只有在同步块结束的时候才会释放锁。在此之前,那个wait
之后被notify
的线程还是BLOCKED。
写了一段代码验证。采用ThreadMXBean来定时Dump线程状态。
public class MTTest {
public static void main(String[] args) {
final ThreadMXBean mxBean = ManagementFactory.getThreadMXBean();
final Object obj = new Object();
final Thread t1 = new Thread(new Runnable(){
@Override
public void run() {
synchronized (obj) {
try {
Thread.sleep(10000);
obj.wait(10000);
System.out.println(Thread.currentThread().getName() + " is done.");
} catch (InterruptedException e) {
}
}
}
});
t1.start();
final Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (obj) {
try {
System.out.println(Thread.currentThread().getName() + " is sleeping ...");
Thread.sleep(10000);
} catch (InterruptedException e) {
}
obj.notify();
try {
System.out.println(Thread.currentThread().getName() + " is sleeping again ...");
Thread.sleep(10000);
System.out.println(Thread.currentThread().getName() + " is done.");
} catch (InterruptedException e) {
}
}
}
});
t2.start();
ScheduledExecutorService es = Executors.newScheduledThreadPool(1);
es.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.print(mxBean.getThreadInfo(t1.getId(), 4));
System.out.print(mxBean.getThreadInfo(t2.getId(), 4));
}
}, 0, 5000, TimeUnit.MILLISECONDS);
}
}
看结果:
"Thread-0" Id=11 TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at com.company.multithread.MTTest$1.run(MTTest.java:22)
at java.lang.Thread.run(Thread.java:745)
"Thread-1" Id=12 BLOCKED on java.lang.Object@69471e3d owned by "Thread-0" Id=11
at com.company.multithread.MTTest$2.run(MTTest.java:37)
- blocked on java.lang.Object@69471e3d
at java.lang.Thread.run(Thread.java:745)
"Thread-0" Id=11 TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at com.company.multithread.MTTest$1.run(MTTest.java:22)
at java.lang.Thread.run(Thread.java:745)
"Thread-1" Id=12 BLOCKED on java.lang.Object@69471e3d owned by "Thread-0" Id=11
at com.company.multithread.MTTest$2.run(MTTest.java:37)
- blocked on java.lang.Object@69471e3d
at java.lang.Thread.run(Thread.java:745)
"Thread-0" Id=11 TIMED_WAITING on java.lang.Object@69471e3d owned by "Thread-1" Id=12
at java.lang.Object.wait(Native Method)
- waiting on java.lang.Object@69471e3d
at com.company.multithread.MTTest$1.run(MTTest.java:23)
at java.lang.Thread.run(Thread.java:745)
Thread-1 is sleeping ...
"Thread-1" Id=12 RUNNABLE
at java.nio.Buffer.position(Buffer.java:246)
at sun.nio.cs.UTF_8.updatePositions(UTF_8.java:77)
at sun.nio.cs.UTF_8.access$200(UTF_8.java:57)
at sun.nio.cs.UTF_8$Encoder.encodeArrayLoop(UTF_8.java:636)
"Thread-0" Id=11 TIMED_WAITING on java.lang.Object@69471e3d owned by "Thread-1" Id=12
at java.lang.Object.wait(Native Method)
- waiting on java.lang.Object@69471e3d
at com.company.multithread.MTTest$1.run(MTTest.java:23)
at java.lang.Thread.run(Thread.java:745)
"Thread-1" Id=12 TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at com.company.multithread.MTTest$2.run(MTTest.java:38)
at java.lang.Thread.run(Thread.java:745)
"Thread-0" Id=11 TIMED_WAITING on java.lang.Object@69471e3d owned by "Thread-1" Id=12
at java.lang.Object.wait(Native Method)
- waiting on java.lang.Object@69471e3d
at com.company.multithread.MTTest$1.run(MTTest.java:23)
at java.lang.Thread.run(Thread.java:745)
"Thread-1" Id=12 TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at com.company.multithread.MTTest$2.run(MTTest.java:38)
at java.lang.Thread.run(Thread.java:745)
Thread-1 is sleeping again ...
"Thread-0" Id=11 BLOCKED on java.lang.Object@69471e3d owned by "Thread-1" Id=12
at java.lang.Object.wait(Native Method)
- blocked on java.lang.Object@69471e3d
at com.company.multithread.MTTest$1.run(MTTest.java:23)
at java.lang.Thread.run(Thread.java:745)
"Thread-1" Id=12 TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at com.company.multithread.MTTest$2.run(MTTest.java:44)
at java.lang.Thread.run(Thread.java:745)
"Thread-0" Id=11 BLOCKED on java.lang.Object@69471e3d owned by "Thread-1" Id=12
at java.lang.Object.wait(Native Method)
- blocked on java.lang.Object@69471e3d
at com.company.multithread.MTTest$1.run(MTTest.java:23)
at java.lang.Thread.run(Thread.java:745)
"Thread-1" Id=12 TIMED_WAITING
at java.lang.Thread.sleep(Native Method)
at com.company.multithread.MTTest$2.run(MTTest.java:44)
at java.lang.Thread.run(Thread.java:745)
Thread-1 is done.
Thread-0 is done.
以上均为自己现阶段对线程状态转换的理解,欢迎交流指正。
以下内容从同事那里贴来,两点比较。
Thread.yield() 方法 VS Thread.sleep() 方法
参数:
- Thread.yield() 方法没有参数
- Thread.sleep() 方法需要参数 毫秒,例如 1000,表示阻塞 1000 毫秒后进入 Ready 状态
线程状态的转变:
- Thread.yield() 方法会导致当前线程从执行状态转变为 RUNNABLE
- Thread.sleep() 方法会导致当前线程从执行状态转变为 WAITING
是否会抛出异常:
- Thread.yield() 方法不会抛出异常
- Thread.sleep() 方法会抛出异常,因此需要 try - catch
线程调度:
- Thread.yield() 只会使得 相同或者更高优先级的其他线程进入运行状态
- Thread.sleep() 方法不考虑优先级,任何的其他线程都可能会进入运行状态
Thread.sleep() 方法 VS obj.wait() 方法
参数:
- Thread.sleep() 方法需要参数 毫秒,例如 1000,表示阻塞 1000 毫秒后进入 Ready 状态
- obj.wait() 方法可以不带参数,也可以带参数
带参数毫秒,例如 1000,表示 在 Waiting Pool 状态中等待 1000 毫秒后进入 Waiting for monitor entry 状态
不带参数,表示 在 Waiting Pool 状态中永久等待,直至其他线程中调用 obj.notify() 或者 obj.notifyAll(),随后进入 Waiting for monitor entry 状态
所在类:
- Thread.sleep() 方法在 Thread 类中,属于静态方法
- obj.wait() 方法在 Object 类中,任何一个对象都可以调用 wait()
目的:
- Thread.sleep() 方法用于线程控制自身流程
- obj.wait() 方法用于线程间通信
线程状态的转变:
- Thread.sleep() 方法会导致当前线程从执行状态转变为WAITING
- obj.wait() 方法会导致当前线程从执行状态转变为Waiting Pool 状态(WAITING)
锁:
- Thread.sleep() 方法不会释放锁
- obj.wait() 方法会释放锁
引用
Java多线程学习(四)等待/通知(wait/notify)机制
Java 线程状态切换图 join,yield,sleep,wait