下面是消费者生产者问题代码,但是该代码无法正常工作。在这里,消费者和生产者应该只是生产和消费一个对象。
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();
}
}
我们得到的输出为:产生
程序挂起。
请帮助提供可能的解决方案/说明
答案 0 :(得分:2)
使用共享锁。在发布的代码中,每个Runnable都将自身用作锁定,因此不会发生实际的锁定。
当一个线程等待时,另一个线程需要调用 same 锁上的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
我认为这是您所期望的。
请参阅this other answer I wrote,以了解队列的简单实现;与将代码放入访问数据结构的线程中相比,最好保护共享数据结构,尤其要看代码编写起来有多容易。
答案 1 :(得分:0)
并发意味着您无法在运行时之前知道哪个线程将首先结束。因此,您不知道哪个消费者和生产者首先启动,执行或完成。
为帮助您,您可以使用循环障碍https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/CyclicBarrier.html或应用Fork / Join框架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的线程池可以帮助您,但是您将失去并发的好处。
答案 2 :(得分:0)
我认为您最能做的就是使用BlockingQueue。