多线程设计模式之保护性暂停模式
定义
保护性暂停模式(Guarded Suspension Design Pattern):当线程在访问某个对象时,发现条件不满足时,就暂时挂起等待条件满足时再次访问。
如果某个结果需要在多线程之间传递,则可以让这些线程关联到一个对象GuardedObject,但是如果这个结果需要不断的从一个线程到另一个线程那么可以使用消息队列(生产者/消费者)
Thread.join()、Future就采用了保护性暂停模式。
简单实现
package com.morris.concurrent.pattern.guardedsuspension.v2;
public class GuardedObject<V> {
private final Object lock = new Object();
private V v;
private boolean isFinished;
public void set(V v) {
synchronized (lock) {
this.v = v;
isFinished = true;
lock.notifyAll();
}
}
public V get() throws InterruptedException {
synchronized (lock) {
while (!isFinished) {
lock.wait();
}
}
return v;
}
}
超时实现
public V get(long millis) {
synchronized (lock) {
while (!isFinished) {
try {
lock.wait(millis);
break;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
return v;
}
上面的超时实现有点小问题。
分析:假如超时时间是5s,但是在第2s的时候别人把这个线程中断了但是此时还没有结果,那么这个线程就会进入下次循环继续等待5s,这样这个线程就等待7s了。
最终实现如下:
public V get(long millis) {
long begin = System.currentTimeMillis(); // 等待开始时间
long waitedTime = 0; // 等待了多长时间
synchronized (lock) {
while (!isFinished) {
if(waitedTime >= millis) {
System.out.println("超时了,不等了");
break;
}
try {
lock.wait(millis - waitedTime); // 继续等待剩余时间
break;
} catch (InterruptedException e) {
waitedTime = System.currentTimeMillis() - begin;
System.out.println("被中断了,等了" + waitedTime + "ms");
}
}
}
return v;
}
使用保护性暂停模式实现Future和Callable
public interface Callable<T> {
T call();
}
public interface Future<T> {
T get();
}
public class FutureTask<T> implements Future<T> {
private boolean isFinish;
private T t;
public synchronized void set(T t) {
this.t = t;
this.isFinish = true;
this.notifyAll();
}
@Override
public synchronized T get() {
while (!isFinish) {
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return t;
}
}
public abstract class Executors {
public static <T> Future<T> newSingleThreadPool(Callable<T> callable) {
FutureTask<T> future = new FutureTask<>();
new Thread(()->{
T t = callable.call();
future.set(t);
}).start();
return future;
}
}