如何使用Semphores在Producer-Consumer中消费?

时间:2016-08-04 02:14:00

标签: java multithreading semaphore

我正在使用Semaphore尝试生产者 - 消费者问题。除了一个地方,这个程序对我来说很好。

public class ProducerConsumerWithSemaphores
{
    private final ArrayList<Integer> list = new ArrayList<>(5);
    private final Semaphore semaphoreProducer = new Semaphore(1);
    private final Semaphore semaphoreConsumer = new Semaphore(0);

    private void produce() throws InterruptedException
    {
        for(int i = 0;i< 5;i++)
        {
            semaphoreProducer.acquire();
            list.add(i);
            System.out.println("Produced: " + i);
            semaphoreConsumer.release();
        }
    }

    private void consumer() throws InterruptedException
    {
        while (!list.isEmpty())    /// This line is where I have the doubt
        {
            semaphoreConsumer.acquire();
            System.out.println("Consumer: " + list.remove(list.size()-1));
            semaphoreProducer.release();
            Thread.sleep(100);
        }
    }

    public static void main(String[] args)
    {
        final ProducerConsumerWithSemaphores obj = new ProducerConsumerWithSemaphores();

        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    obj.produce();
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }).start();

        new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    obj.consumer();
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

在获取信号量之前,是否可以检查列表是否为空?这会在多线程环境中引起任何问题吗?

2 个答案:

答案 0 :(得分:1)

private void consumer() throws InterruptedException
{
    while (!list.isEmpty())    /// This line is where I have the doubt

问题是,如果消费者比生产者跑得快,你的消费者立即退出,那么你没有消费者!!

正确的例子看起来像, Producer–consumer problem#Using semaphores。我相信你的意图不是使用true作为无限循环,因为你希望生产者/消费者在工作完成后退出。如果这是你的意图,你可以1.设置一个totalCount来结束循环。 <击> 2。或者boolean标志,当生产者放置最后一个时,生产者将在putItemIntoBuffer之后设置该标志。该标志必须受到保护以及buffer。(更新:如果有多个生产者/消费者,此方法不起作用)3。模拟EOF(从producer - consume; how does the consumer stop?获取的想法)< / p>

  

这会在多线程环境中引起任何问题吗?

您的关键部分(您的list)未受到保护。通常我们使用3个信号量。第三个用作保护缓冲区的互斥锁。

停止生产者/消费者,
方法1的示例代码:

public class Test3 {

  private Semaphore mutex = new Semaphore(1);
  private Semaphore fillCount = new Semaphore(0);
  private Semaphore emptyCount = new Semaphore(3);

  private final List<Integer> list = new ArrayList<>();

  class Producer implements Runnable {

    private final int totalTasks;

    Producer(int totalTasks) {
      this.totalTasks = totalTasks;
    }

    @Override
    public void run() {
      try {
        for (int i = 0; i < totalTasks; i++) {
          emptyCount.acquire();
          mutex.acquire();
          list.add(i);
          System.out.println("Produced: " + i);
          mutex.release();
          fillCount.release();
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  class Consumer implements Runnable {
    private final int totalTasks;

    Consumer(int totalTasks) {
      this.totalTasks = totalTasks;
    }

    @Override
    public void run() {
      try {
        for (int i = 0; i < totalTasks; i++) {
          fillCount.acquire();
          mutex.acquire();
          int item = list.remove(list.size() - 1);
          System.out.println("Consumed: " + item);
          mutex.release();
          emptyCount.release();
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  public void runTest() {
    int numProducer = 3;
    int tasksPerProducer = 10;
    int numConsumer = 6;
    int tasksPerConsumer = 5;

    for (int i = 0; i < numProducer; i++) {
      new Thread(new Producer(tasksPerProducer)).start();
    }
    for (int i = 0; i < numConsumer; i++) {
      new Thread(new Consumer(tasksPerConsumer)).start();
    }
  }

  public static void main(String[] args) throws IOException {
    Test3 t = new Test3();
    t.runTest();
  }
}

方法3的示例代码:

public class Test4 {

  private Semaphore mutex = new Semaphore(1);
  private Semaphore fillCount = new Semaphore(0);
  private Semaphore emptyCount = new Semaphore(3);

  private Integer EOF = Integer.MAX_VALUE;

  private final Queue<Integer> list = new LinkedList<>(); // need to put/get data in FIFO

  class Producer implements Runnable {

    private final int totalTasks;

    Producer(int totalTasks) {
      this.totalTasks = totalTasks;
    }

    @Override
    public void run() {
      try {
        for (int i = 0; i < totalTasks + 1; i++) {
          emptyCount.acquire();
          mutex.acquire();
          if (i == totalTasks) {
            list.offer(EOF);
          } else {
            // add a valid value
            list.offer(i);
            System.out.println("Produced: " + i);
          }
          mutex.release();
          fillCount.release();
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  class Consumer implements Runnable {

    @Override
    public void run() {
      try {
        boolean finished = false;
        while (!finished) {
          fillCount.acquire();
          mutex.acquire();
          int item = list.poll();
          if (EOF.equals(item)) {
            // do not consume this item because it means EOF
            finished = true;
          } else {
            // it's a valid value, consume it.
            System.out.println("Consumed: " + item);
          }
          mutex.release();
          emptyCount.release();
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }

  public void runTest() {
    int numProducer = 3;
    int tasksPerProducer = 10;

    for (int i = 0; i < numProducer; i++) {
      new Thread(new Producer(tasksPerProducer)).start();
    }

    int numConsumer = numProducer; // producers will put N EOFs to kill N consumers.
    for (int i = 0; i < numConsumer; i++) {
      new Thread(new Consumer()).start();
    }
  }

  public static void main(String[] args) throws IOException {
    Test4 t = new Test4();
    t.runTest();
  }
}

答案 1 :(得分:0)

为什么不使用单个信号量而不是使用两个信号量,以便在线程link之间进行同步

另外,您可以使用ArrayBlockingQueue,它们是线程安全的,可以正确演示生产者消费者问题。

相关问题