[英]AtomicBoolean not getting updated
我正在實施標准的生產者-消費者計划,以使生產者在生產200種產品后停止運轉。 為了表明這一點,生產者將-1放入BlockingQueue變量,否則該變量始終包含正整數。 我的消費者實現如下:
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();
}
}
}
替代的消費者邏輯(仍然出現相同的問題):
@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();
}
}
}
執行此操作時,我的使用者線程被卡在queue.take()
,就像他們在等待隊列中的產品可用一樣。 一種可能是:所有使用者線程同時檢查條件isProducerClosed.get()
,進入while循環,並訪問queue.take()
方法。 這是正確的假設嗎? 如果是,是否有任何方法可以在不使用低級synchronized
關鍵字的情況下實現此目的? 我用volatile boolean
變量嘗試了同樣的事情,結果是完全一樣的。 只有將變量設為靜態后,我才能看到所有使用者在隊列中遇到-1后都被終止(因為var現在是類擁有的)。
我的通話代碼:
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.");
}
}
生產者代碼:
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--;
}
}
}
一種可能是:所有使用者線程同時檢查條件isProducerClosed.get(),進入while循環,並訪問queue.take()方法。 這是正確的假設嗎? 如果是,是否有任何方法可以在不使用低級同步關鍵字的情況下實現此目的? 我用volatile布爾變量嘗試了同樣的事情,結果是完全一樣的。 只有將變量設為靜態后,我才能看到所有使用者在隊列中遇到-1后都被終止(因為var現在是類擁有的)。
是的,這個假設是正確的。
第一個問題是: isProducerClosed
在消費者之間共享。 它必須在消費者之間共享,這樣,如果一個消費者設定了它的價值,其他消費者也可以看到該價值。 使其static
使其共享,因此此后情況有所改善
第二個問題:即使在共享isProducerClosed
之后,您也可能會遇到多個使用者將在空隊列上執行queue.take()
情況(一個線程可能獲取最后一個值,而另一個線程則在第一個執行之前執行了take()
將isProducerClosed
設置為true)。 您將需要對此進行同步(例如,使用雙重檢查)
示例代碼(仍然包含使用者以外的部分中的錯誤/競賽)-
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.