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; } }