同步模式-保护性暂停
- 定义:一个线程等待另一个线程的执行结果
- 要点:
- 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个GuardedObject
- 如果有结果不断从一个线程到另一个线程可以使用消息队列
- JDK中,join的实现、future的实现,采用的保护性暂停
-
因为要等待另一方的结果,因此归类到同步模式
保护性暂停.png
- 优点:
- 使用join()必须等待另外一个线程结束,才能继续执行,保护性暂停可以在对象中设置值后唤醒等待的线程,继续下面操作
- 使用join,等待变量必须设置为全局的,采用保护性暂停可以设置为局部
@Slf4j(topic = "ants.ProtectiveSuspension")
public class ProtectiveSuspension {
public static void main(String[] args) {
GuardObject guardObject = new GuardObject();
new Thread("t1"){
@SneakyThrows
@Override
public void run() {
log.debug("t1获取值");
log.debug("保护性暂停获得值:{}",guardObject.get(2000));
}
}.start();
new Thread("t2"){
@SneakyThrows
@Override
public void run() {
log.debug("t2设置值");
Thread.sleep(2000);
guardObject.set(1);
}
}.start();
}
}
class GuardObject {
private Object object;
public Object get(long time) throws InterruptedException {
// 虚假唤醒
synchronized (this) {
long beginTime = System.currentTimeMillis();
long timeWait = 0;
while (object == null&&timeWait<= time) {
this.wait(time-timeWait);
timeWait=System.currentTimeMillis()-beginTime;
}
}
return object;
}
public void set(Object o) {
synchronized (this) {
this.object = o;
this.notifyAll();
}
}
}
join()
- 在Thread中的join方法,使用保护性暂停来实现。
异步模式-生产者/消费者
- 要点
- 与保护性暂停不一样,不需要产生结果和消费结果的线程一一对应
- 消费队列可以用来平衡生产和消费的线程资源
- 生产者仅负责生产数据,不关心数据该如何处理,而消费者专心处理结果数据
- 消息队列是由容量限制的,满时不会加入数据,空时不会再消耗数据
- JDK中的各种阻塞队列,采用的就是这种模式

Park & Unpark
- park和unpark是LockSupport类中的方法
- LockSupport.park(); 暂停当前线程
- LockSupport.unpark(暂停线程对象); 恢复某个线程的运行
- unpark()可以在park()之前或之后运行,都可以让wait的线程继续执行
- 与Object的wait¬ify相比
- wait、notify和notify必须配合Object Monitor一起使用,而park和unpark不必
- park和unpark是以线程为单位来【阻塞】和【唤醒】线程,而notify只能随机唤醒一个等待线程,notifyAll是唤醒所有等待的线程,不能很准确的唤醒指定线程
- park&unpark中unpark可以先执行,而wait¬ify中不能限制性notify
@Slf4j(topic = "ants.TestParkUnpark")
public class TestParkUnpark {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread("t1") {
@SneakyThrows
@Override
public void run() {
log.debug("park 之前代码");
Thread.sleep(1000);
LockSupport.park();
log.debug("park 后面代码");
}
};
t1.start();
Thread.sleep(2000);
log.debug("main 调用unpark");
LockSupport.unpark(t1);
}
}
park&unpark原理
- 每个线程都有自己的一个park对象,由三部分组成_counter,_cond和_mutex
- parker对象:每个先传给你都有自己的parker对象
- _counter 状态只有0或1
- _mutex 对象,里面有等待队列(_cond)
- _cond 条件变量

- 当前线程调用Unsafe.park()方法
- 检查_counter,本情况为0,这是,获得_mutex互斥锁
- 线程进入_cond条件变量
-
设置_counter=0
image.png
- 调用Unsafe.unpark(Thread_0)方法,设置_counter为1
- 唤醒_cond条件变量中的Therad_0
- Thread_0回复运行
-
设置_counter为0
image.png
- 调用Unsafe.unpark(Thread_0)方法,设置_counter为1
- 当前线程调用Unsafe.park()方法
- 检查_counter,本情况为1,这时线程无需阻塞,继续运行
- 设置_counter为0
网友评论