用 “volatile 标记位的停止方法“ 不适合的场景

 

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

}

 

上一篇:8.20面试小结


下一篇:5.ribbon