线程生成器java中的消费者
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程生成器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以唤醒等待的线程。我们从您的日志记录中知道生产者线程做了它的事情,但由于通知作用于与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可以看到一个简单的队列实现;您最好不要保护共享数据结构,而不是将代码放在访问数据结构的线程中,尤其要看代码编写的容易程度。
并发意味着您无法在运行时知道哪个Thread将首先结束。因此,您无法知道哪个消费者和生产者首先被启动,执行或完成。
为了帮助您,您可以使用循环屏障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();
它将等到执行runProducer以执行runConsumer()的numThreadsToWait一样多的线程。
也许使用大小为1的线程池可以帮助你,但你将失去并发的好处。
我认为你能做的最好的就是使用BlockingQueue。
以上是关于线程生成器java中的消费者的主要内容,如果未能解决你的问题,请参考以下文章