- BLOCKED状态
- 一、是什么
- 二、什么情况下会使线程进入BLOCKED状态
- 1. 等待监视器锁进入同步块/方法
- 1)监视器锁
- 监视器锁是什么?
- MonitorEnter进入(注2)
- MonitorExit退出(注2)
- 2)解读 - 等待监视器锁进入同步块/方法
- synchroinzed原理分析
- 3)示例
- 2. 调用object.wait后重新进入一个同步块/方法
- 1)示例
- WAITING状态
- 一、是什么
- 二、什么情况下会进入
- wait/notify/notifyAll
- 示例
- 原理
- Object.wait
- Object.notify
- Object.notifyAll
- Thread.join
- 示例
- 原理
- LockSupport.park
- 示例
- 原理
- unpark(源码见注5)
- 两个状态之间的区别(总结)
- 扩展
BLOCKED是线程的一种状态,Thread.state中对其的介绍:
BLOCKED
翻译:
等待监视器锁的线程阻塞的线程状态。一个处于阻塞状态的线程正在等待一个监视器锁进入一个同步的块/方法或在调用Object.wait之后重新进入一个同步的块/方法
理解:表示线程当前正在等待获取到锁进入同步块/方法
二、什么情况下会使线程进入BLOCKED状态解读上述翻译内容,线程有两种情况:①等待监视器锁进入同步块/方法;②调用object.wait后重新进入一个同步块/方法。
1. 等待监视器锁进入同步块/方法 1)监视器锁 监视器锁是什么?任何一个对象都有一个Monitor与之关联,当且一个Monitor被持有后,它将处于锁定状态。Synchronized在JVM里的实现都是
基于进入和退出Monitor对象来实现方法同步和代码块同步,虽然具体实现细节不一样,但是都可以通过成对的MonitorEnter和
MonitorExit指令来实现。(注1)
每个对象都是一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
- 如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者;
- 如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1;
- 如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权;
思考:为什么允许重新进入?
执行monitorexit的线程必须是对象引用所对应的monitor的所有者。指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。 其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。
2)解读 - 等待监视器锁进入同步块/方法等待监视器锁进入同步块/方法,也就是使用synchroinzed关键字标注某个类或者某个方法,标注的代码部分就是同步块/方法,** 等待进入该方法的线程的状态就是BLOCKED阻塞状态 **。
synchroinzed原理分析synchroinzed本质是给当前类或指定某个对象加锁。结合监视器锁的概念总结,synchroinzed是在编译时** 对这个类添加ACC_SYNCHRONIZED标志 ** 或者 ** 在方法代码内容前后添加monitorenter 和 monitorexit 指令 **,利用Monitor监视器锁实现方法与代码块同步。(注3)
对象加锁的情况:
private static Object object = new Object();
public static void testSync(){
synchronized(object) {
System.out.println("当前占用线程: " + Thread.currentThread().getName());
try {
Thread.sleep(100000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
对象加锁字节码打印:
思考:为什么会有两个monitorexit?
类加锁的情况:
public static synchronized void testSync(){
System.out.println("当前占用线程: " + Thread.currentThread().getName());
try {
Thread.sleep(100000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
类加锁字节码打印:
以类锁情况为例:
package com.example.demo.thread;
public class BlockAndWait {
public static synchronized void testSync(){
System.out.println("当前占用线程: " + Thread.currentThread().getName());
try {
Thread.sleep(100000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Thread a = new Thread(()->{
testSync();
}, "Thread-A");
Thread b = new Thread(()->{
testSync();
}, "Thread-B");
a.start();
b.start();
System.out.println("Thread-A 状态:" + a.getState());
System.out.println("Thread-B 状态:" + b.getState());
try {
Thread.sleep(100000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程状态打印:
jstack线程信息:
解读:①要调用object.wait;②调用之后要重新进入:notify之后继续执行wait之后的逻辑代码;
可知,BLOCKED状态是由于线程Object.wait被唤醒后未及时获取到锁,从而进入阻塞状态。
public class BlockAndWait {
private static Object object = new Object();
public static void testSync(){
try {
synchronized(object) {
System.out.println("test2-obj-当前占用线程: " + Thread.currentThread().getName());
object.wait();
System.out.println("test2-obj-唤醒后: " + Thread.currentThread().getName());
Thread.sleep(1000000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void testSync3(){
try{
synchronized(object) {
object.notify();
System.out.println("test3-当前占用线程: " + Thread.currentThread().getName());
Thread.sleep(1000000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Thread a = new Thread(()->{
testSync();
}, "Thread-A");
Thread b = new Thread(()->{
testSync3();
}, "Thread-B");
a.start();
b.start();
System.out.println("Thread-A 状态:" + a.getState());
System.out.println("Thread-B 状态:" + b.getState());
try {
Thread.sleep(100000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
线程状态打印:
jstack线程信息:
WAITING状态 一、是什么
WAITING
翻译:
二、什么情况下会进入 wait/notify/notifyAll 示例等待线程的线程状态。由于调用了以下方法之一,线程处于等待状态:①无超时等待的Object.wait;②无超时连接的Thread.join;③LockSupport.park
处于等待状态的线程正在等待另一个线程执行特定的操作。
例如,在一个对象上调用了object. wait()的线程正在等待另一个线程在该对象上调用object. notify()或object. notifyall();调用thread .join()的线程正在等待指定的线程终止。
public static void testWait(){
try{
synchronized(object) {
System.out.println("testWait-当前占用线程: " + Thread.currentThread().getName());
object.wait();
System.out.println("testWait被唤醒,执行业务代码");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void testNotify(){
try{
synchronized(object) {
System.out.println("testNotify-当前占用线程: " + Thread.currentThread().getName());
Thread.sleep(100000);
System.out.println("testNotify唤醒wait");
object.notify();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Thread a = new Thread(()->{
testWait();
}, "Thread-A");
Thread b = new Thread(()->{
testNotify();
}, "Thread-B");
a.start();
b.start();
System.out.println("Thread-A 状态:" + a.getState());
System.out.println("Thread-B 状态:" + b.getState());
try {
Thread.sleep(100000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
原理
Object.wait
public final void wait() throws InterruptedException {
wait(0);
}
Object.notify
public final native void notify();
Object.notifyAll
public final native void notifyAll();
Thread.join
示例
public class JoinDemo {
private static void testA() {
System.out.println("testA执行");
try {
Thread.sleep(1000000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
private static void testB() {
System.out.println("testB执行");
}
public static void main(String[] args) {
Thread a = new Thread(()->{
testA();
}, "Thread-A");
Thread b = new Thread(()->{
testB();
try {
a.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}, "Thread-B");
a.start();
b.start();
try {
Thread.sleep(6000);
System.out.println("Thread-A 状态:" + a.getState());
System.out.println("Thread-B 状态:" + b.getState());
Thread.sleep(100000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
jstack中可以看出join的本质也是通过wait进行的处理。
LockSupport.park 示例public class LockSDemo {
public static void main(String[] args) {
Thread a = new Thread(()->{
LockSupport.park();
}, "Thread-A");
a.start();
try {
Thread.sleep(6000);
System.out.println("Thread-A 状态:" + a.getState());
Thread.sleep(100000000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
日志打印:
线程信息:
LockSupport:java.util.concurrent.locks.LockSupport
public static void park() {
UNSAFE.park(false, 0L);
}
翻译:
为线程调度目的禁用当前线程,除非许可可用。
如果许可证是可用的,那么它就会被使用,并且呼叫立即返回;否则,当前线程将被禁用,并处于休眠状态,直到发生以下三种情况之一:
其他一些线程以当前线程为目标调用unpark;或其他线程中断当前线程;或调用伪返回(也就是说,没有原因)。
此方法不报告其中哪些导致方法返回。调用者应该重新检查导致线程停止的条件。例如,调用者还可以确定线程返回时的中断状态。
那么,根据AQS源码以及说明描述中的** 当前线程 ,可以明白,unpark需要指定到 当前线程 才能唤醒park阻塞的 当前线程 **。
unpark(源码见注5)
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
翻译:
两个状态之间的区别(总结)使给定线程的许可可用(如果它还不可用)。如果线程在park被阻塞,那么它将解除阻塞。否则,它的下一次调用park保证不会被阻塞。如果给定的线程没有启动,这个操作根本不能保证有任何效果。
参数:
线程——要取消驻留的线程,或者为null,在这种情况下,此操作不起作用
- BLOCKED是被动阻塞当前线程,WAITING是主动阻塞当前线程
- BLOCKED依赖synchroinzed,WAITING不依赖
- BLOCKED状态当锁释放后就会解除,但WAITING需要被唤醒或者等待超时后才会解除
Hashtable(synchroinzed)、ConcurrentHashMap(synchroinzed+cas)、condition(wait/notify)、reentrantlock(AQS-LockSupport.park/unpark)、JUC工具类(wait/notify)
参考文档:
注1:什么是monitor和Monitor监视器锁
注2:Monitor监视器锁原理
注3:synchronized底层原理
注4:wait/notify原理分析
注5:LockSupport.park/unpark



