java 的线程状态共有6种,在Thread.State
枚举中定义:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43public 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
状态;
测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37public 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后会超时,超时后就可以直接获取到锁,然后执行完毕。
继续测试:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32public 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锁:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36ReentrantLock 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线程内容请搜索靠谱客的其他文章。
发表评论 取消回复