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在调用wait20s后会超时,超时后就可以直接获取到锁,然后执行完毕。
继续测试:
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会在调用await20s后从await结束调用,因为无法获取锁,依然进入同步队列,转为WAITING状态。
最后附上状态图:

本文参考
https://blog.51cto.com/14267003/2447783
最后
以上就是丰富钢笔最近收集整理的关于JAVA线程的六个状态和转换关系的全部内容,更多相关JAVA线程内容请搜索靠谱客的其他文章。
发表评论 取消回复