简体   繁体   English

AtomicBoolean没有更新

[英]AtomicBoolean not getting updated

I'm implementing a standard Producer-Consumer program such that producer stops after producing 200 products. 我正在实施标准的生产者-消费者计划,以使生产者在生产200种产品后停止运转。 To signal this, producer puts -1 to the BlockingQueue variable, which otherwise always contains positive integers. 为了表明这一点,生产者将-1放入BlockingQueue变量,否则该变量始终包含正整数。 My consumer implementation is as below : 我的消费者实现如下:

public class Consumer implements Runnable{
private BlockingQueue<Integer> queue;
private AtomicBoolean isProducerClosed = new AtomicBoolean(false);

public Consumer(BlockingQueue<Integer> queue) {
    this.queue = queue;
}

@Override
public void run() {

    try {
        Thread.sleep(50);

        while(!isProducerClosed.get()) {
            try {
                Integer value = queue.take();

                if ((value.intValue() == -1)){
                    System.out.println(Thread.currentThread().getName() + " Encountered -1. Signal to shutdown consumers.");
                    //isProducerClosed.set(true);
                    isProducerClosed.compareAndSet(false, true);
                    break;
                }

                System.out.println(Thread.currentThread().getName() + " consuming : " + value);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    } catch (InterruptedException e) {
        e.printStackTrace();
    }


}

} }

Alternate consumer logic (still getting same issue) : 替代的消费者逻辑(仍然出现相同的问题):

@Override
public void run() {

    while(true) {
        try {
            Thread.sleep(50);
            Integer value = null;

            synchronized (this) {
                if(!isProducerClosed.get()) {
                    value = queue.take();
                    if ((value.intValue() == -1)) {
                        System.out.println(Thread.currentThread().getName() + " Encountered -1. Signal to shutdown consumers.");
                        isProducerClosed.set(true);
                        isProducerClosed = isProducerClosed;
                        System.out.println("Current value of isProducerClosed : " + isProducerClosed.get());
                    }
                }
            }

            if (!isProducerClosed.get()) {
                System.out.println(Thread.currentThread().getName() + " consuming : " + value);
            } else {
                break;
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

While executing this, my consumer threads get stuck on queue.take() as if they are waiting for a product to be available in the queue. 执行此操作时,我的使用者线程被卡在queue.take() ,就像他们在等待队列中的产品可用一样。 One possibility could be : all consumer threads checked the condition isProducerClosed.get() at the same time, entered the while loop, and access queue.take() method. 一种可能是:所有使用者线程同时检查条件isProducerClosed.get() ,进入while循环,并访问queue.take()方法。 Is this correct assumption? 这是正确的假设吗? If yes, is there any way to implement this without using low level synchronized keyword? 如果是,是否有任何方法可以在不使用低级synchronized关键字的情况下实现此目的? I tried the same thing with volatile boolean variable and the result was exactly same. 我用volatile boolean变量尝试了同样的事情,结果是完全一样的。 Only after making that variable static, was i able to see all consumers getting terminated after encountering -1 in the queue (as the var is now class-owned). 只有将变量设为静态后,我才能看到所有使用者在队列中遇到-1后都被终止(因为var现在是类拥有的)。

My calling code : 我的通话代码:

public class ProducerConsumer {

private BlockingQueue<Integer> sharedQueue = new ArrayBlockingQueue<Integer>(10);


public void executeProducerConsumer(int producerCount, int consumerCount){
    ExecutorService executorService = Executors.newFixedThreadPool(3, Executors.defaultThreadFactory());

    for(int i = 0; i < producerCount; i++) {
        executorService.submit(new Producer(sharedQueue));      //producer
    }

    for(int i = 0; i < consumerCount; i++) {
        executorService.submit(new Consumer(sharedQueue));      //i-th consumer.
    }


    //initiates clossure of threads after completion, in async manner.
    executorService.shutdown();

    //wait till all threads are done.
    try {
        executorService.awaitTermination(1, TimeUnit.HOURS);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("The End.");
}

} }

Producer code : 生产者代码:

public class Producer implements Runnable {
private BlockingQueue<Integer> queue;
private volatile int maxNumberOfItemsToProduce = 10;

public Producer(BlockingQueue<Integer> queue) {
    this.queue = queue;
}

@Override
public void run() {
    Random random = new Random();

    while(true){

        if(maxNumberOfItemsToProduce == 0) {
            try {
                queue.put(-1);
                System.out.println("Terminating Producer after producing max number of products.");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            break;
        }

        try {
            queue.put(random.nextInt(300));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


        maxNumberOfItemsToProduce--;
    }

}

} }

Screenshot of console: 控制台的屏幕截图: 在此处输入图片说明

One possibility could be : all consumer threads checked the condition isProducerClosed.get() at the same time, entered the while loop, and access queue.take() method. 一种可能是:所有使用者线程同时检查条件isProducerClosed.get(),进入while循环,并访问queue.take()方法。 Is this correct assumption? 这是正确的假设吗? If yes, is there any way to implement this without using low level synchronized keyword? 如果是,是否有任何方法可以在不使用低级同步关键字的情况下实现此目的? I tried the same thing with volatile boolean variable and the result was exactly same. 我用volatile布尔变量尝试了同样的事情,结果是完全一样的。 Only after making that variable static, was i able to see all consumers getting terminated after encountering -1 in the queue (as the var is now class-owned). 只有将变量设为静态后,我才能看到所有使用者在队列中遇到-1后都被终止(因为var现在是类拥有的)。

Yes, the assumption is true. 是的,这个假设是正确的。

First problem is : isProducerClosed is NOT shared across consumers. 第一个问题是: isProducerClosed在消费者之间共享。 It has to be shared across consumers, so that if one comsumer sets its value, other consumers can see that value too. 它必须在消费者之间共享,这样,如果一个消费者设定了它的价值,其他消费者也可以看到该价值。 Making it static makes it shared and hence the situation improves after that 使其static使其共享,因此此后情况有所改善

Second problem: even after isProducerClosed is shared, you may get into a situation where multiple consumers would execute queue.take() on a empty queue (a thread may take the last value, but another thread by execute take() before the first one sets isProducerClosed to true). 第二个问题:即使在共享isProducerClosed之后,您也可能会遇到多个使用者将在空队列上执行queue.take()情况(一个线程可能获取最后一个值,而另一个线程则在第一个执行之前执行了take()isProducerClosed设置为true)。 You will need to synchronize this (eg by using double-checking) 您将需要对此进行同步(例如,使用双重检查)

Sample code (still contains bugs/races in parts other than the consumer) - 示例代码(仍然包含使用者以外的部分中的错误/竞赛)-

public class TestClass {

    private BlockingQueue<Integer> sharedQueue = new ArrayBlockingQueue<Integer>(10);

    public static void main(String[] args) {
        TestClass t = new TestClass();
        t.executeProducerConsumer(3, 3);
    }

    public void executeProducerConsumer(int producerCount, int consumerCount) {
        ExecutorService executorService = Executors.newFixedThreadPool(producerCount + consumerCount, Executors.defaultThreadFactory());

        for (int i = 0; i < producerCount; i++) {
            executorService.submit(new Producer(sharedQueue));      //producer
        }

        for (int i = 0; i < consumerCount; i++) {
            executorService.submit(new Consumer(sharedQueue));      //i-th consumer.
        }

        //initiates clossure of threads after completion, in async manner.
        executorService.shutdown();

        //wait till all threads are done.
        try {
            executorService.awaitTermination(1, TimeUnit.HOURS);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("The End.");
    }

}

class Consumer implements Runnable {

    private BlockingQueue<Integer> queue;
    private static volatile boolean isProducerClosed = false; // make this static so that it is shared across consumers

    public Consumer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            Thread.sleep(50);
            Integer value;

            while (!isProducerClosed) {
                try {
                    synchronized (queue) { //synchronize so that only one thread can talk to the queue at a time
                        if (!isProducerClosed) { //double check
                            value = queue.take(); // we can now safely take an item
                            if ((value.intValue() == -1)) {
                                isProducerClosed = true;
                                System.out.println(Thread.currentThread().getName() + " Encountered -1. Signal to shutdown consumers.");
                                break;
                            }
                        } else {
                            System.out.println(Thread.currentThread().getName() + " Last item was taken by some other consumer. Exiting!");
                            break;
                        }
                    }
                    consumeValue(value); //Consume the value outside the lock
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }


    }

    private void consumeValue(Integer value) {
        System.out.println(Thread.currentThread().getName() + " Consuming value :" + value);
    }
}

class Producer implements Runnable {

    private BlockingQueue<Integer> queue;
    private static volatile int maxNumberOfItemsToProduce = 10;

    public Producer(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        Random random = new Random();

        while (true) {

            if (maxNumberOfItemsToProduce == 0) {
                try {
                    queue.put(-1);
                    System.out.println("Terminating Producer after producing max number of products.");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                break;
            }

            try {
                queue.put(random.nextInt(300));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            maxNumberOfItemsToProduce--;
        }

    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM