1. 定义
Balking (犹豫)模式用在一个线程发现另一个线程或本线程已经做了某一件相同的事,那么本线程就无需再做了,直接结束返回
2. 实现
例如:
public class MonitorService {
// 用来表示是否已经有线程已经在执行启动了
private volatile boolean starting;
public void start() {
log.info("尝试启动监控线程...");
synchronized (this) {
if (starting) {
return;
}
starting = true;
}
// 真正启动监控线程...
}
}
当前端页面多次点击按钮调用 start 时
输出
[http-nio-8080-exec-1] cn.itcast.monitor.service.MonitorService - 该监控线程已启动?(false)
[http-nio-8080-exec-1] cn.itcast.monitor.service.MonitorService - 监控线程已启动...
[http-nio-8080-exec-2] cn.itcast.monitor.service.MonitorService - 该监控线程已启动?(true)
[http-nio-8080-exec-3] cn.itcast.monitor.service.MonitorService - 该监控线程已启动?(true)
[http-nio-8080-exec-4] cn.itcast.monitor.service.MonitorService - 该监控线程已启动?(true)
它还经常用来实现线程安全的单例
public final class Singleton {
private Singleton() {
}
private static Singleton INSTANCE = null;
public static synchronized Singleton getInstance() {
if (INSTANCE != null) {
return INSTANCE;
}
INSTANCE = new Singleton();
return INSTANCE;
}
}
不为空了直接返回,为空才创建,这就是懒惰模式。
对比一下保护性暂停模式:保护性暂停模式用在一个线程等待另一个线程的执行结果,当条件不满足时线程等待。
3.改进两阶段终止模式的代码
我在执行某件事的时候,发现别人已经把这件事做了,那我就不做了,这就叫Balking(犹豫模式)
我们结合前面的两阶段终止的代码,我们发现,每一个线程都把执行监控记录的代码执行了一遍,三个线程就执行了三遍,没必要,这时候我们这样改:
设置一个变量start,我们给这个start加锁,保证代码只执行一遍,第一遍的时候执行,并修改start的值,以后再来的代码,我么就不会再执行监控线程了。
@Slf4j(topic = "c.TwoPhaseTermination")
public class Test13 {
public static void main(String[] args) throws InterruptedException {
TwoPhaseTermination tpt = new TwoPhaseTermination();
tpt.start();
tpt.start();
tpt.start();
Thread.sleep(3500);
log.debug("停止监控");
tpt.stop();
}
}
@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination {
// 监控线程
private Thread monitorThread;
// 停止标记
private volatile boolean stop = false; // volatile保证变量在多线程之间的可见性
// 判断是否执行过 start 方法
private boolean starting = false; // Balking
// 启动监控线程
public void start() {
synchronized (this) { // balking
if (starting) { // false
return;
}
starting = true;
}
monitorThread = new Thread(() -> {
while (true) {
Thread current = Thread.currentThread();
// 是否被打断
if (stop) {
log.debug("料理后事");
break;
}
try {
Thread.sleep(1000);
log.debug("执行监控记录");
} catch (InterruptedException e) {
current.interrupt();
}
}
}, "monitor");
monitorThread.start();
}
// 停止监控线程
public void stop() {
stop = true;
monitorThread.interrupt();
}
}