概述
java 的线程状态共有6种,在Thread.State
枚举中定义:
public enum State {
/**
* 创建后尚未启动的线程
*/
NEW,
/**
* 可能正在执行,也可能正在等操作系统分配执行时间
*/
RUNNABLE,
/**
* 阻塞状态
* 在等待获取一个排它锁。调用了Object.wait()后又调用了Object.notify()后也会进入该状态
*/
BLOCKED,
/**
* 无限等待状态,不会被分配处理器执行时间,需要其他线程显式唤醒
* 下面的方法会进入该状态:
* Object#wait() 没有超时的
* Thread#join() 没有超时的
* LockSupport#park() 没有超时的
*/
WAITING,
/**
* 超时等待。同上,但在指定时间内会由系统自动唤醒。以下方法会进入该状态
* Thread#sleep(time)
* Object#wait() 指定超时
* LockSupport#parkNanos()
* Thread#join() 指定超时
* LockSupport#parkUntil()
*/
TIMED_WAITING,
/**
* 线程终止
*/
TERMINATED;
}
在当前线程调用其他线程T的Thread.join()
或thread.join(long timeout)
,当前线程会进入WAITING
或TIMED_WAITING
状态,当且不会释放持有的锁对象。其他线程T执行完毕或者timeout时间到,当前线程如果获取到锁,则进入RUNNABLE
状态,否则进入BLOCKED
状态,因为join
也是基于wait
的;
为了便于讲解,将锁分为两类:synchronized
锁和AQS
锁【内部基于LockSupport#park
系列】
二者内部都基于一个同步队列【如AQS】和等待队列【如Condition】
针对synchronized
:
-
内部调用了
Object#wait
、Object#wait(timeout)
或condition#await
、condition#await(timeout)
都是先进入等待队列,当被notify()
或signal
或超时后才会进入同步队列。 -
BLOCKED
状态 只有处于synchronized
同步队列的时候才会有该状态。当其调synchronized
没有获得锁时,会进入同步队列,转为该状态. -
调用
synchronized
如果立刻获得了锁,则进入RUNNABLE
状态; -
synchronized
内部调用Object#wait
、Object#wait(timeout)
会分别进入WAITING
和TIMED_WAITING
状态,进入等待队列; -
当上面处于
Object#wait
、Object#wait(timeout)
被调用notify()
或超时后立即获取到锁时,进入RUNNABEL
状态。 -
针对5,如果没有获取到锁,则进入同步队列,转为
BLOCKED
状态
针对重入锁
如ReetrantLock
:
- 执行
lock#lock()
时,如果立即获取到了锁,则进入RUNNABLE
状态; - 对于1,果没有获取到锁,内部会调用
LockSupport.park
,进入同步队列,转为WAITING
状态 - 重入锁内调用
Condition#await()
时,进入等待队列,转为WAITING
状态; - 调用
Condition#await(timeout)
时,同上,但转为TIMED_WAITING
状态; - 对于调用了3和4,如果调用了
signal
或4超时,且立即获取到了锁,则进入RUNNABLE
状态;否则进入同步队列,转为WAITING
状态;
测试
public class ThreadState {
public static void main(String[] args) {
Object obj=new Object();
new Thread(()->{
synchronized (obj){
try {
System.out.println("线程1调用sleep(10s)");
Thread.sleep(10000L);
System.out.println("线程1结束调用sleep(10s)");
System.out.println("线程1调用wait()");
obj.wait();
System.out.println("线程1结束wait()");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"thread——1").start();
new Thread(()->{
synchronized (obj){
System.out.println("线程2调用notify");
// obj.notify();
System.out.println("线程2结束nofity");
try {
Thread.sleep(10000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"thread——2").start();
}
}
推测:
- 线程1线获取到
obj
锁,然后睡眠10s,此时线程1应该是TIMED_WAITING
状态,而线程2因为调用synchronized
没有获取到锁,应该进入BLOCKED
状态。
运行后,点击相机图标【Dump Threads】,可以看到线程状态:
-
10s后,线程1会调用
obj.wait()
方法,此时,线程1会释放obj
锁,进入WAITING
状态;而线程2此时就会获取到锁,【这里先不调用
notify
】,线程2继续睡眠10s,此时线程2应该处于TIMED_WAITING
状态
- 10s后,线程2释放锁,但线程1因为没有被
notify
也没有超时,因此会一直处于WAITING
状态。 - 这时,如果将线程1修改为await的超时调用
obj.wait(20000L);
,则线程1在睡眠后会进入TIMED_WAITING
状态,等到线程2执行完毕后会释放锁,线程1在调用wait
20s后会超时,超时后就可以直接获取到锁,然后执行完毕。
继续测试:
public class ThreadState {
public static void main(String[] args) {
Object obj=new Object();
new Thread(()->{
synchronized (obj){
try {
System.out.println("线程1调用wait()");
obj.wait();
System.out.println("线程1结束wait()");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"thread——1").start();
new Thread(()->{
synchronized (obj){
System.out.println("线程2调用notify");
obj.notify();
System.out.println("线程2结束nofity");
try {
Thread.sleep(10000000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"thread——2").start();
}
}
放开线程2的obj.notify()
,并让线程2睡眠较长时间。
分析:
1.线程1线获取到锁,执行obj.wait
进入WAITING
状态;
- 线程2获取到锁,执行
obj.notify
,然后睡眠,会进入TIMED_WAITING
状态,但是执行notify和sleep都不会释放锁,只是从等待队列转移到同步队列; - 线程1从
await
结束调用,但是因为线程2还没释放锁,因此线程1会进入BLOCKED
状态。
只看下线程1的:
那怎么看RUNNABLE
状态呢?将线程2的睡眠改成一个死循环后,再看线程2的状态:
再来测试下AQS锁:
ReentrantLock lock=new ReentrantLock();
Condition condition = lock.newCondition();
new Thread(()->{
try {
System.out.println("线程1调用lock.lock()");
lock.lock();
System.out.println("线程1结束lock.lock()");
System.out.println("线程1调用await");
condition.await();
System.out.println("线程1结束调用await");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("线程1调用unlock");
lock.unlock();
}
},"thread——1").start();
new Thread(()->{
try {
System.out.println("线程2调用lock");
lock.lock();
System.out.println("线程2结束调用lock");
System.out.println("线程2调用signal");
// condition.signal()
// ;
Thread.sleep(100000000000L);
System.out.println("线程2结束调用signal");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
},"thread——2").start();
}
分析:
- 线程1获取到锁后,调用
condition.await()
,会进入等待队列,转为WAITING
状态,并释放锁; - 线程2此时获取到锁,然后无限睡眠,线程2进入
TIMED_WAITING
状态。
如果将线程2的condition.signal()
打开,那么会怎么样?
线程2调用signal
后,进入睡眠,依然不会释放锁,此时线程1从await
结束调用,但因为线程2还持有锁,所以线程1依然无法获取锁,会进入同步队列,然后还是WAITING
状态【注意和synchronized
区分,synchronized
在这里会进入BLOCKED
状态】
在此基础上,将线程1改成condition.await(20000L)
会怎样呢?
首先,线程1会先为TIMED_WAITING
状态,在线程2调用signal
后,因为没有仍然无法获取到锁,会进入WAITING
状态,或者线程2不调用signal
,在线程1会在调用await
20s后从await结束调用,因为无法获取锁,依然进入同步队列,转为WAITING
状态。
最后附上状态图:
本文参考
https://blog.51cto.com/14267003/2447783
最后
以上就是丰富钢笔为你收集整理的JAVA线程的六个状态和转换关系的全部内容,希望文章能够帮你解决JAVA线程的六个状态和转换关系所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复