这段代码无法退出循环?
@Slf4j
public class juc14 {
static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while(flag) {
// ...
}
}).start();
Thread.sleep(1000);
log.info("停止");
flag = false;
}
}
分析
解决方法:
方法一:
volatile static boolean flag = true;
volatile(易变关键字)
它可以用来修饰成员变量和静态成员变量,他可以避免线程从自己的工作缓存中查找变量的值,必须到主存中获取它的值,线程操作volatile变量都是直接操作主存
方法二:
synchronized
@Slf4j
public class juc14 {
static boolean flag = true;
final static Object lock = new Object();
public static void main(String[] args) throws InterruptedException {
new Thread(() -> {
while(true) {
synchronized (lock) {
if(!flag){
break;
}
}
}
}).start();
Thread.sleep(1000);
log.info("停止");
synchronized (lock){
flag = false;
}
}
}
保证可见性一般推荐volatile,较synchronized更轻量级
注意
synchronized语句块既可以保证代码块的原子性,也同时保证代码块内变量的可见性。但缺点是synchronized是属于重量级操作,性能相对更低
如果在前面示例的死循环中加入 System.out.println()会发现即使不加volatile修饰符,线程t也能正确看到对run变量的修改了,想一想为什么?I
用volatile改进两阶段终止代码
/**
* 两阶段终止改进
*/
@Slf4j
class TwoStateTerminatedImprove {
private Thread monitorThread;
private volatile boolean stop = false;
public void start() {
monitorThread = new Thread(() -> {
while (true) {
Thread t = Thread.currentThread();
if (stop) {
log.info("料理后事");
break;
}
try {
Thread.sleep(500);
log.info("执行监控代码");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"monitor");
monitorThread.start();
}
public void stop() {
stop = true;
monitorThread.interrupt();
}
}
@Slf4j
class test01 {
public static void main(String[] args) {
TwoStateTerminatedImprove tstp = new TwoStateTerminatedImprove();
tstp.start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("停止监控");
tstp.stop();
}
}
Balking模式
/**
* ==============================================================
* Balking 犹豫模式
*
* 定义:Balking (犹豫)模式用在一个线程发现另一个线程或本线程已经做了某一件相同的事,
* 那么本线程就无需再做了,直接结束返回
*
* 场景: 1. 保证监控线程只启动一次
*/
@Slf4j
class HesitatePattern {
private Thread monitorThread;
private volatile boolean stop = false;
// 是否调用过start(),保证只调用一次start
private boolean started = false;
public void start() {
synchronized (this) {
if (started) {
return;
}
started = true;
}
monitorThread = new Thread(() -> {
while (true) {
Thread t = Thread.currentThread();
if (stop) {
log.info("料理后事");
break;
}
try {
Thread.sleep(500);
log.info("执行监控代码");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, "monitor");
monitorThread.start();
}
public void stop() {
stop = true;
monitorThread.interrupt();
}
}
@Slf4j
class test02 {
public static void main(String[] args) {
HesitatePattern hp = new HesitatePattern();
hp.start();
hp.start();
}
}