Java中的线程生产者使用者

时间:2018-08-23 19:19:19

标签: java multithreading producer-consumer

下面是消费者生产者问题代码,但是该代码无法正常工作。在这里,消费者和生产者应该只是生产和消费一个对象。

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

    }
}

我们得到的输出为:产生

程序挂起。

请帮助提供可能的解决方案/说明

3 个答案:

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

它将一直等到有numThreadsT​​oWait个已执行runProducer来执行runConsumer()的线程。

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

答案 2 :(得分:0)

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