我在多线程中很天真,并且正在尝试学习它的概念。这是我对Producer-Consumer问题的实现。如果它不正确/粗糙/任何其他可以改善我的设计的建议,请查看并建议我。
static int data = 0;
static Object obj1 = new Object();
static class Producer implements Runnable {
public void run() {
produce();
}
void produce() {
while (true) {
if (data < 5){
synchronized(obj1){
System.out.println("Producing Data. Now Data is "+data++);
obj1.notifyAll();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else{
try {
System.out.println("Producer inactive");
synchronized(obj1){
obj1.wait();
}
System.out.println("Producer active");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
static class Consumer implements Runnable{
public void run(){
consume();
}
void consume() {
while (true) {
if (data > 0){
synchronized(obj1){
System.out.println("Consuming Data. Now Data is "+data--);
obj1.notifyAll();
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
else{
try {
System.out.println("Consumer Inactive");
synchronized(obj1){
obj1.wait();
}
System.out.println("Consumer Active");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
答案 0 :(得分:1)
好几点。生产者和消费者通常共享数据结构。静态数据的使用非常奇怪,坦率地说没有意义。通常,您要共享的是数据结构,如生产者和消费者之间的队列。生产者将把东西添加到队列的尾部,消费者将从队列的头部(FIFO - 先出先出)中抽取东西。现在我没有看到这一点,那么究竟是什么产生消费?
良好的生产者消费者体系结构并不太关心交换什么类型的数据,因此您可以在其上传递许多不同类型的数据。这就是面向对象命令架构将帮助您的地方。在此示例中,SomeMessage表示某个对象层次结构的根,因此可以交换各种消息。
以下是如何在程序中实例化Producer-Consumer架构的简单示例:
public class SomeClient {
public void start() {
Queue sharedQueue = new LinkedList();
producer = new Producer( sharedQueue );
consumer = new Consumer( sharedQueue );
producer.start();
consumer.start();
}
}
以下是其实施:
public class Producer implements Runnable {
Thread thread;
Queue queue;
public Producer(Queue queue) {
this.queue = queue;
}
public void start() {
thread = new Thread(this);
thread.start();
}
public void shutdown() {
thread.interrupt(); // request a shutdown
thread.join(); // make sure we wait until Producer.thread exits before this thread continues
}
public void run() {
try {
while( !Thread.isInterrupted() ) {
SomeMessage message = produceAMessage();
synchronized( queue ) {
queue.add( message );
queue.notifyAll();
}
}
} catch( InterruptedException ex ) {
System.out.println("Producer shutting down per request.");
} finally {
thread = null;
}
}
}
public class Consumer implements Runnable {
Thread thread;
Queue queue;
public Consumer( Queue queue ) {
this.queue = queue;
}
public void start() {
thread = new Thread( this );
thread.start();
}
public void shutdown() {
thread.interrupt(); // request a shutdown
thread.join(); // make sure we wait until Consumer.thread exits before this thread continues
}
public void run() {
try {
while( !thread.isInterrupted() ) {
SomeMessage message = take();
doSomethingWithMessage( message );
}
} catch( InterruptedException ex ) {
System.out.println("Stop processing - consumer per request.");
} finally {
thread = null;
}
}
private SomeMessage take() throws InterruptedException {
synchronized( queue ) {
queue.wait();
return queue.remove();
}
}
}
这个实现中有两点不同。 Producer和Consumer共享一个Queue实例,他们使用该实例执行同步调用。这样就不会在没有拥有锁的情况下从该结构写入或读取。在他们添加到队列(生产者)或从队列(消费者)中删除后,他们不需要使用同步。它们可以自由处理而无需相互通信。他们通过添加尾部和从头部绘制来在每个实例之间交换SomeMessage的实例。
take()方法在此代码中非常重要。如果没有辅助方法,则无法处理消息并释放锁定。这一点非常重要,以便您的消费者可以接收消息并放开锁定以允许其他生产者/消费者在此特定消费者处理消息时添加/删除消息。这样可以尽可能快地保持吞吐量。
是的,我说生产者。这种架构允许多个生产者和多个消费者,而无需更改生产者或消费者的内部。
请注意,捕获InterruptedException不在while循环中。如果您想要一个可以完全关闭的可预测程序,这一点非常重要。 InterruptedException和中断的概念是表现良好的Java线程的核心。如果您不知道在什么条件下生成此异常,您将永远不会理解Java中的多线程应用程序。这不是随机发生的。无法以编程方式停止Java线程。另一个线程必须请求它自己中断。并且线程必须遵守请求,否则它将不会停止。所以,如果我们得到一个。关掉。在这个程序中,我们只会在调用wait或notify时获取它,这意味着在我们处理消息时我们不会被中断。消费者将在停止前完成消息处理。
最后,考虑到Java中的并发库,实现Producer-Consumer关系实际上要容易得多,但这是一个很好的例子,说明如何在最低级别的Java上实现它以了解这些库正在为您做什么。
答案 1 :(得分:0)
封装消费和生产行为可以更加可重用。在下面的代码中,我将消费者/生产者线程中的共享资源同步问题解耦,这可能有助于解决像对象池和连接池等类似问题。
import java.util.LinkedList;
import java.util.Queue;
public class ProducerConsumer {
public static void main(String[] args) {
SyncQueue syncQueue = new SyncQueue(1);
Producer producer = new Producer(syncQueue , 10);
Consumer consumer = new Consumer(syncQueue,10);
producer.start();
consumer.start();
}
}
class SyncQueue {
private Queue<Integer> queue = new LinkedList<Integer>();
private Integer size;
public SyncQueue(Integer size) {
super();
this.size = size;
this.signalledBefore = false;
}
public synchronized void put(Integer data){
while(queue.size() == size){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
queue.add(data);
notifyAll();
}
public synchronized Integer get(){
while(queue.isEmpty()){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Integer data = queue.remove();
notifyAll();
return data;
}
}
class Producer extends Thread{
private SyncQueue syncQueue;
private Integer size;
public Producer(SyncQueue syncQueue, Integer size) {
this.syncQueue = syncQueue;
this.size = size;
}
@Override
public void run() {
for (Integer i = 0; i < size; i++) {
syncQueue.put(i);
System.out.println("Produced:" + i);
try {
sleep((int)Math.random()*100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Consumer extends Thread{
private SyncQueue syncQueue;
private Integer size;
public Consumer(SyncQueue syncQueue, Integer size) {
this.syncQueue = syncQueue;
this.size = size;
}
@Override
public void run() {
for (Integer i = 0; i < size; i++) {
try {
sleep((int)Math.random()*100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Consumed:" + syncQueue.get());
}
}
}