[英]Threads producer consumer in java
下面是消費者生產者問題代碼,但是該代碼無法正常工作。 在這里,消費者和生產者應該只是生產和消費一個對象。
public class ProducerConsumer {
private static LinkedList<Integer> linkedList = new LinkedList<>();
public static void main(String a[]) throws InterruptedException {
Thread producer = new Thread(new Runnable() {
@Override
public void run() {
synchronized(this) {
while (linkedList.size() == 1) {
try {
wait();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Produced");
linkedList.add(1);
notify();
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread consume = new Thread(new Runnable() {
@Override
public void run() {
// produce
synchronized(this) {
while (linkedList.isEmpty()) {
try {
wait();
} catch(InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Consumed");
linkedList.removeFirst();
notify();
try {
Thread.sleep(1000);
} catch(InterruptedException e) {
e.printStackTrace();
}
}
}
});
producer.start();
consume.start();
producer.join();
consume.join();
}
}
我們得到的輸出為:
然后程序掛起。
請提供可能的解決方案/解釋的幫助
使用共享鎖。 在發布的代碼中,每個Runnable都將自身用作鎖定,因此不會發生實際的鎖定。
當一個線程等待時,另一個線程需要在同一鎖上調用notify才能喚醒等待的線程。 從您的日志記錄中我們知道Producer線程會執行其操作,但是由於notify所作用的鎖與Consumer使用的鎖不同,因此Consumer線程永遠不會喚醒。
更改代碼以使用共享鎖的工作原理是:
import java.util.*;
public class ProducerConsumer { private static LinkedList linkedList = new LinkedList();
public static void main(String a[]) throws InterruptedException {
final Object lock = new Object();
Thread producer = new Thread(new Runnable() {
@Override
public void run() {
synchronized (lock) {
while (linkedList.size() ==1) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Produced");
linkedList.add(1);
lock.notify();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
Thread consume = new Thread(new Runnable() {
@Override
public void run() {
// produce
synchronized (lock) {
while (linkedList.isEmpty()) {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("Consumed");
linkedList.removeFirst();
lock.notify();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
producer.start();
consume.start();
producer.join();
consume.join();
}
}
輸出為:
c:\example>java ProducerConsumer
Produced
Consumed
我想這就是您的期望。
順便說一句, 我看到了我為隊列的簡單實現而寫的另一個答案 。 與將代碼放在訪問數據結構的線程中相比,保護共享數據結構更好,尤其要看代碼編寫起來有多容易。
並發意味着您無法在運行時之前知道哪個線程將首先結束。 因此,您不知道哪個消費者和生產者首先啟動,執行或完成。
為了幫助您,您可以使用循環屏障https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html或應用Fork / Join Framework https:// docs。 oracle.com/javase/tutorial/essential/concurrency/forkjoin.html
您的同步塊只是說:一次只能有一個線程可以執行這部分代碼,而不能執行第一個和第二個之后的代碼。
CyclicBarrier的工作方式示例:
service = Executors.newFixedThreadPool(numThreadsTotal);
CyclicBarrier c = new CyclicBarrier(numThreadsToWait);
runProducer();
c.await();
runConsumer();
它將等待,直到有與numThreadsToWait一樣多的已執行runProducer的線程來執行runConsumer()。
也許使用大小為1的線程池可以幫助您,但是您將失去並發的好處。
我認為您能做的最好是使用BlockingQueue 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.