我正在网络搜索生产者消费者问题,我得到了this link。
程序员在这里使用 Vector 作为sharedqueue
。
我想为什么我需要一个同步块,因为 Vector 已经是线程安全的。它必须自己处理线程。
但是当我试图删除同步块时。它给了我一个IllegalMonitorStateException
。以下是生产方法
private void produce(int i) throws InterruptedException {
//wait if queue is full
while (sharedQueue.size() == SIZE) {
// synchronized (sharedQueue) {
System.out.println("Queue is full " + Thread.currentThread().getName()
+ " is waiting , size: " + sharedQueue.size());
sharedQueue.wait();
// }
}
//producing element and notify consumers
// synchronized (sharedQueue) {
sharedQueue.add(i);
sharedQueue.notifyAll();
// }
}
我的问题是为什么我们需要同步或锁定已经线程安全的对象?
答案 0 :(得分:2)
如果没有同步,你可以这样做:
Producer Thread Consumer Thread
if (sharedQueue.isEmpty) {
sharedQueue.add(i);
sharedQueue.notifyAll();
sharedQueue.wait()
}
因此,即使队列中有数据,消费者线程也可能永远等待,因为在测试数据之间添加了东西,并且它开始等待有关添加数据的通知。
当然有一些解决方法,例如wait
上的超时,但这些都有轮询开销,轮询间隔导致的延迟,丑陋的代码等等。当你可以使用同步时,没有理由诉诸这些事情。
那么为什么队列类是线程安全的呢?好吧,线程安全类不是魔法!线程安全只是意味着,对该类实例的单个方法调用是线程安全的。因此,例如,如果两个线程同时对同一个实例执行add
,则不会损坏任何内容,使用另一个执行操作擦除一个操作,或者类似的任何操作。
但是如果你有多个线程进行多个操作,那么它们可以是交错的,这毕竟是许多线程和先发制人的多任务处理的重点!那么交错操作会发生什么,这取决于操作。例如,许多仅执行add
的线程会以某种未指定的顺序添加内容,但这可能没问题。但是当您不希望操作以“随机”顺序发生时,您需要使用同步。
如上例所示,在这种情况下,即使队列中的数据,“随机”顺序也可能导致消费者无限期地等待。事实上,如果你有序列“做修改,通知服务员”,服务员做“看看有没有做的事情,否则等待”,你有同样的问题,并且必须使用修改通知和查等待。
你得到错误,因为Java要求你在通知时拥有锁,因为如上所述,没有锁就没有意义,它总是一个错误,一个错误。这不是特定于Java的,这是这种机制的基础,它们总是需要锁定,在Wikipedia article section阅读更多内容。