生产者/消费者和调试中死锁的原因

时间:2014-03-30 12:05:05

标签: java multithreading

虽然以下是众所周知的话题,但我想要你的想法。 我写了一个小程序如下:所有的生产者排队等候消费者。我不明白为什么会这样。它可以完全阻止的场景是什么。

让我们考虑生产者/消费者正在等待锁定阵列以及使消费者/生产者退出同步块的原因。我的意思是它必须缓慢移动,但绝不能发生死锁。我相信。

这里我有两个问题: 1.发生死锁的场景是什么? 2.如何理解引擎盖下发生的事情。我的意思是如何调试。

public class ProducerConsumer implements Runnable {

    boolean producer = false;

    private volatile int i = 0;

    int[] array = new int[10];

    public static String getThreadName() {
        return Thread.currentThread().getName();
    }

    public void producer() {
        try {
            synchronized (array) {
                while (i > 9) {
                    System.out.println("Producer of " + getThreadName()
                            + " is waiting i " + i);
                    array.wait();
                    System.out.println("Producer of " + getThreadName()
                            + " came out of sleep i " + i);
                }
                System.out.println("Producer of " + getThreadName()
                        + " in synchronized block i" + i);
                array[i] = generateRandom();
                System.out.println("Producer of " + getThreadName()
                        + " inserted in array " + array[i] + " index " + i);
                i++;
                array.notifyAll();
            }
            Thread.sleep(100);
        } catch (InterruptedException e) {
            System.out.println("Producer of " + getThreadName()
                    + " interrupted " + e);
        }

    }

    public void consumer() {
        try {
            synchronized (array) {
                while (i < 0) {
                    System.out.println("Consumer of " + getThreadName()
                            + " is waiting i " + i);
                    array.wait();
                    System.out.println("Consumer of " + getThreadName()
                            + " came out of sleep i " + i);
                }
                System.out.println("Consumer of " + getThreadName()
                        + " in synchronized block extracted value " + array[i]
                        + " of index " + i);
                i--;
                array.notifyAll();
            }
            Thread.sleep(100);
        } catch (InterruptedException e) {
            System.out.println("Consumer of " + getThreadName()
                    + " interrupted " + e);
        }

    }

    public static int generateRandom() {
        Random random = new Random();
        return random.nextInt(10);
    }

    public static void main(String[] args) {
        ProducerConsumer pc = new ProducerConsumer();
        for (int i = 0; i < 4; i++) {
            if (i % 2 == 0)
                new Thread(pc, "producer thread").start();
            else {
                new Thread(pc, "consumer thread").start();
            }
        }
    }

    public void run() {
        while (true) {
            if (getThreadName().equalsIgnoreCase("producer thread")) {
                producer();
            } else {
                consumer();
            }
        }

    }
}

如下所示:

Consumer of consumer thread in synchronized block extracted value 0 of index 0
Producer of producer thread in synchronized block i-1
Producer of producer thread in synchronized block i-1
Consumer of consumer thread is waiting i -1
Consumer of consumer thread is waiting i -1

2 个答案:

答案 0 :(得分:2)

您的代码在许多地方都不正确。

我希望所有线程都能完成异常,因为

  • IllegalMonitorException(在ProducerConsumer对象上调用notify()) 但是这个ProducerConsumer对象没有同步块)
  • ArrayIndexOfBoundsException(我可以在produce()方法中变为10)

您检查过错误输出吗?

答案 1 :(得分:0)

Java通过其java.util.concurrent包提供了一个简洁的并发程序实现。因此,您应该使用Concurrent API以更安全的方式处理锁定,而不是尝试重新发明轮子并将其全部弄错。这是Producer-Consumer的模拟:

import java.util.Random;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * We want a Producer thread to create random values, and the Consumer thread to
 * consume it. One caveat is that if the Producer has already created a random
 * value, which the Consumer thread hasn't consumed yet, the Producer thread
 * blocks or waits. On the flip side, the Consumer thread waits for the Producer
 * thread to produce some value if the Producer thread hasn't already.
 * <p/>
 * Write a Program to simulate such a situation.
 */

public class ProducerConsumerCommunication
{
  private volatile boolean running = true;
  private ArrayBlockingQueue<Integer> buffer = new ArrayBlockingQueue<>(1);
  private Random random = new Random(System.currentTimeMillis());

  public ProducerConsumerCommunication()
  {
    ExecutorService service = Executors.newCachedThreadPool();
    service.execute(new ProducerTask());
    service.execute(new ConsumerTask());
    service.shutdown();
  }

  public static void main(String[] args)
  {
    new ProducerConsumerCommunication();
  }

  private class ProducerTask implements Runnable
  {
    public void run()
    {
      while (running)
      {
        try
        {
          Thread.sleep(random.nextInt(2000));
          Integer value = random.nextInt();
          buffer.put(value); // Blocks if buffer is full.
          System.out.println("Value Put: " + value);
        }
        catch (InterruptedException e)
        {
          e.printStackTrace();
        }
      }
    }
  }

  private class ConsumerTask implements Runnable
  {
    public void run()
    {
      while (running)
      {
        try
        {
          Thread.sleep(random.nextInt(2000));
          Integer value = buffer.take(); // Blocks if buffer is empty.
          System.out.println("Value Taken: " + value);
        }
        catch (InterruptedException e)
        {
          e.printStackTrace();
        }
      }
    }
  }
}

尝试运行它并亲自了解使用Concurrent API实现此类方案的简单性和直观性。它还可以保持代码清洁,让您专注于手头的问题。

Producer Consumer问题死锁的原因并不多。如果一个线程锁定object A并等待object B上的锁被释放,而其他线程同时锁定object B并等待锁定{ {1}}被释放,出现了僵局。