线程生成器java中的消费者

问题描述 投票:1回答:3

下面是使用者生产者问题代码,但代码未按预期工作。在这里,消费者和生产者应该只是生产和消费一个对象。

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();

    }
}

我们得到的输出为:生产

程序挂起了。

请帮助解决可能的解决方案/解释

java multithreading producer-consumer
3个回答
2
投票

使用共享锁。在发布的代码中,每个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可以看到一个简单的队列实现;您最好不要保护共享数据结构,而不是将代码放在访问数据结构的线程中,尤其要看代码编写的容易程度。


0
投票

并发意味着您无法在运行时知道哪个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()的numThreadsT​​oWait一样多的线程。

也许使用大小为1的线程池可以帮助你,但你将失去并发的好处。


0
投票

我认为你能做的最好的就是使用BlockingQueue

© www.soinside.com 2019 - 2024. All rights reserved.