消费者无法使用我的Java生产者/消费者/队列简单代码

时间:2018-12-14 20:18:56

标签: java multithreading queue polling producer-consumer

我正在尝试用Java 11实现一个简单的生产者/消费者系统。基本上,我每个人都有两个线程,外加一个全局队列,如下所示:

  • 全局优先级队列。
  • 第一个线程生产者运行HTTP服务器,侦听传入的http消息,并在收到消息后pushes将其作为队列中的作业(以queue.size递增)
  • 第二个线程(使用者)连续peeks进入队列。如果有工作(job ! = null),则在某处提交HTTP请求,并在成功接收后,从队列中轮询该请求(queue.size()递减)。

骨架如下:

主类:

public class Manager
{
    private Consumer consumer;
    private Producer producer;
    Queue queue;

    public static void main (String args[])
    {
        consumer = new Consumer();
        producer = new Producer();
    }
} 

生产者类别:

public class Producer implements Runnable
{
    public Producer()
    {
        Thread producer = new Thread(this);
        producer.start();
    }

    public void run()
    {
            //HTTP server starts, listens, and adds to the queue upon receiving a Job
            server.start();
            Manager.queue.add(new Job());
    }
}

消费类:

public class Consumer implements Runnable
{
    public Consumer()
    {
        Thread consumer = new Thread(this);
        consumer.start();
    }

    public void run()
    {
    // Thread.sleep(1);

        while(true)
        {
            //get an object off the queue
            Job job= Manager.queue.peek();
            //do some stuff with the object
        }
    }
}

Producerqueue都可以正常工作。但是问题出在Consumer上。上面的使用者代码(带有while(true)循环)不会窥视该项目。但是,当我在Thread.sleep(x)循环之前添加while(true)时,即使x=1 ms仍然有效,并且可以成功捕获该项目。

出什么问题了?从理论上讲,while(true)循环应该不是问题!为什么看不到和peek物品?!

2 个答案:

答案 0 :(得分:1)

问题的原因:队列之间的读写不同步。

这里发生的是,在不同CPU内核上运行的两个线程都使用它们自己的队列副本,因此生产者可能正在添加内容,并且这些更改甚至可能传播到RAM中,但是使用者从未检查任何东西在RAM中,由于它拥有该队列的缓存副本,因此女巫保持为空。

Thread.sleep()之所以行之有效,是因为唤醒时,线程必须从RAM那里获取所有可能的内容。

正确的方法是仅在同步队列时访问队列,如下所示:

在制作人中:

synchronized(Manager.queue) {
     Manager.queue.add(new Job());
}

和消费者:

boolean continue = true;
while (continue) {
    synchronized(Manager.queue) {
        Job job=Manager.queue.pop();
    }
}

最后一点:while (true)的效率极低,您可以使用Object.wait()Object.notify()

在制作人中:

synchronized(Manager.queue) {
     Manager.queue.add(new Job());
     Manager.queue.notify();
}

和消费者:

boolean continue = true;
while (continue) {
    synchronized(Manager.queue) {
        while (Manager.queue.peek() == null) {
            Manager.queue.wait();
        }
        Job job=Manager.queue.pop();
    }
}

答案 1 :(得分:1)

PriorityQueue不是线程安全的,而PriorityBlockingQueue 。只要您没有使用BlockingQueue接口中定义的任何方法,这两种实现都是可以互换的。只需将PriorityQueue更改为PriorityBlockingQueue应该可以解决您的问题。