package com.mzj.thread.interrupt;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* 用 “volatile 标记位的停止方法“ 不适合的场景
*
* @author muzhongjiang 2021-08-12
**/
public class VolatileCanStop {
public static void main(String[] args) throws InterruptedException {
ArrayBlockingQueue<Integer> storage = new ArrayBlockingQueue<>(8);
Producer producer = new Producer(storage);
Thread producerThread = new Thread(producer);
producerThread.start();
Thread.sleep(500);
Consumer consumer = new Consumer(storage);
while (consumer.needMoreNums()) {
System.out.println(consumer.storage.take() + "被消费了");
Thread.sleep(100);
}
System.out.println("消费者不需要更多数据了。");
/**
* 一旦Consumer不需要更多数据了,我们应该让Producer也停下来,但是实际情况却停不下来。
* 当消费者不再需要数据,就会将 canceled 的标记位设置为 true,理论上此时生产者会跳出 while 循环,并打印输出“生产者运行结束”。
* 然而尽管已经把 canceled 设置成 true,在某种情况下生产者仍然没有停止:生产者在执行 storage.put(num) 时发生阻塞,在它被叫醒之前是没有办法进入下一次while判断 canceled 的值的,
* 所以在这种情况下用 volatile 是没有办法让生产者停下来的,
* 相反如果用 interrupt 语句来中断,即使生产者处于阻塞状态,仍然能够感受到中断信号(因为put内部使用了await,会抛InterruptedException)。
* */
producer.canceled = true;// <<<<<<<<<<<<<<<<<<<<<<<<<<<
System.out.println(producer.canceled);
}
}
class Producer implements Runnable {
public volatile boolean canceled = false;
public BlockingQueue<Integer> storage;
public Producer(BlockingQueue<Integer> storage) {
this.storage = storage;
}
@Override
public void run() {
int num = 0;
try {
while (num <= 100000 && !canceled) {
if (num % 50 == 0) {
storage.put(num);
System.out.println(num + "是50的倍数,被放到仓库中了。");
}
num++;
}
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("生产者结束运行");
}
}
}
class Consumer {
public BlockingQueue<Integer> storage;
public Consumer(BlockingQueue<Integer> storage) {
this.storage = storage;
}
public boolean needMoreNums() {
if (Math.random() > 0.97) {
return false;
}
return true;
}
}