使用信号量的制作人/消费者;陷入僵局

时间:2012-04-04 22:14:02

标签: java multithreading mutex semaphore

根据http://en.wikipedia.org/wiki/Producer-consumer_problem我想使用信号量来模拟P / C问题。我遇到了僵局,我不知道是什么问题。

public static void main(String[] args) {
        CustomBlockingQueue blockingQueue = new CustomBlockingQueue();
        new Thread(new Producer(blockingQueue)).start();
        new Thread(new Consumer(blockingQueue)).start();
    }
}

@SuppressWarnings("serial")
class CustomBlockingQueue extends LinkedList<Object> {
    private static final int MAX_SIZE = 10;

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

    @Override
    public boolean offer(Object e) {
        try {
            mutex.acquire();
        } catch (InterruptedException e2) {
            e2.printStackTrace();
        }
        boolean result = super.offer(e);
        System.out.println("offer " + size());
        try {
            fillCount.release();
            emptyCount.acquire();
            mutex.release();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }
        return result;
    }

    @Override
    public Object poll() {
        try {
            mutex.acquire();
        } catch (InterruptedException e2) {
            e2.printStackTrace();
        }
        Object result = super.poll();
        System.out.println("poll  " + size());
        try {
            emptyCount.release();
            fillCount.acquire();
            mutex.release();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return result;
    }
}

class Producer implements Runnable {
    private CustomBlockingQueue blockingQueue;
    private Random random = new Random();

    public Producer(CustomBlockingQueue blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                TimeUnit.SECONDS.sleep(random.nextInt(2));
                blockingQueue.offer(new Object());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class Consumer implements Runnable {
    private CustomBlockingQueue blockingQueue;
    private Random random = new Random();

    public Consumer(CustomBlockingQueue blockingQueue) {
        this.blockingQueue = blockingQueue;
    }

    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) {
            try {
                TimeUnit.SECONDS.sleep(random.nextInt(4));
                blockingQueue.poll();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

使用信号量

信号量解决了唤醒呼叫丢失的问题。在下面的解决方案中,我们使用两个信号量fillCount和emptyCount来解决问题。 fillCount是要在缓冲区中读取的项目数,emptyCount是缓冲区中可以写入项目的可用空间数。当新项目被放入缓冲区时,fillCount递增并且emptyCount递减。如果生产者尝试在其值为零时减少emptyCount,则生产者将进入休眠状态。下次使用一个项目时,emptyCount会递增,生产者会被唤醒。消费者的工作方式类似。

2 个答案:

答案 0 :(得分:2)

您可以考虑使用BlockingQueue代替互斥锁并等待您。

除此之外,我还有一个旧页面,它展示了生产者/消费者的竞争条件(与虚假中断相反)。但我的实现不使用信号量,所以我不确定它会对你有所帮助:

  

http://256stuff.com/gray/docs/misc/producer_consumer_race_conditions/

答案 1 :(得分:2)

您的锁定顺序错误:

需要提供:

        emptyCount.acquire();
        mutex.acquire();
        doModification();
        mutex.release();
        fillCount.release();

民意调查需要进行类似的更改:

        fillCount.acquire();
        mutex.acquire();
        doModification();
        mutex.release();
        emptyCount.release();

在你的实现中,你正在等待信号量,同时持有导致问题的互斥锁,因为另一个线程可以等待互斥锁以释放信号量。