我正在使用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();
}
}
在获取信号量之前,是否可以检查列表是否为空?这会在多线程环境中引起任何问题吗?
答案 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,它们是线程安全的,可以正确演示生产者消费者问题。