我正在尝试熟悉wait()和notify()方法,并且我已经编写了一个简单的类,就像一个简单的生产者 - 消费者运动的监视器,其中有N个生产者和N个消费者。无论如何,根据练习中的要求,监视器只能存储3个项目。所以生产者必须等待;相反,如果显示器中有0个项目,消费者必须等待。
public class Monitor {
private List<Integer> items;
private int capacity;
private Object waitProducer;
private Object waitConsumer;
private int counter;
public Monitor() {
this.items = new ArrayList<Integer>();
this.capacity = 3;
this.waitProducer = new Object();
this.waitConsumer = new Object();
this.counter = 0;
}
public void produce() throws InterruptedException {
synchronized (this) {
if (this.items.size() == this.capacity) {
synchronized (this.waitProducer) {
System.out.println("Producer " + Thread.currentThread().getId() + " aspetta");
this.waitProducer.wait(); /***/
}
}
counter++;
System.out.println("Thread " + Thread.currentThread().getId()
+ " produces object " + counter);
this.items.add(counter);
synchronized (this.waitConsumer) {
this.waitConsumer.notify();
}
}
}
public void consume() throws InterruptedException {
synchronized (this) {
if (this.items.size() == 0) {
synchronized (this.waitConsumer) {
this.waitConsumer.wait(); /***/
}
}
System.out.println("Thread " + Thread.currentThread().getId()
+ " consume object " + this.items.get(0));
this.items.remove(0);
synchronized (this.waitProducer) {
this.waitProducer.notify();
}
}
}
}
我认为在调用wait()方法时,使用/ *** /:infact存在问题,然后线程会在waitProducer(或waitConsumer)上释放锁,但它不在Monitor对象上。这就是为什么当第一个产生(第一个消费者)时调用wait()然后Monitor对象不再可获得。显然,对Monitor对象的访问必须互斥,以便正确更新列表和计数器。那么,正确的方法是什么?谢谢
答案 0 :(得分:3)
一次只有一个线程可以在synchronized
块内执行代码。
此外,当在该对象上调用synchronized(object)
时,必须获得对象的独占锁定(通过wait()
)。
这意味着,解锁/解除阻塞/通知代码必须可以被称为wait()
的原始块之外的其他线程访问。否则,执行将永远等待。
在一个线程等待this.waitConsumer.notify();
时,代码中没有其他线程可以到达this.waitProducer.wait();
。因为所有这些都包含在synchronized(this)
块中,所以其他线程将继续争夺锁定this
。
答案 1 :(得分:1)
@james large如果两个线程在不同的对象上同步,那么这两个线程可以同步在同一个块中是什么意思?
假设您有一个synchronized
块,如下所示:
synchronized(obj) {
...body of synchronized block...
}
...whatever happens next...
当Java线程T执行该语句时,它首先通过计算表达式obj
来开始。该表达式必须返回null
,否则必须返回对象的引用。如果它是null
,那么将抛出NullPointerException。否则,线程T将尝试锁定对象。
没有两个线程可以同时锁定同一个对象。如果对象已被某个其他线程锁定,则在对象解锁之前,线程T将无法继续。
当线程T最终能够锁定对象时,它将执行同步块的主体,然后它将解锁对象并继续进行接下来发生的任何事情。
这里的重要概念是表达式obj
。假设线程T计算obj
,获取对象O的引用,锁定它,并进入synchronized块。然后线程U出现,评估obj
,得到不同的对象,P。线程U将锁定对象P,并且能够进入同步块,而线程T仍在那里在同一时间。
那怎么会发生呢?
如果有意,可能会是这样的:
class MyClass {
private final Object lock = new Object();
void foobar() {
synchronized(lock) { ... }
}
}
如果线程T和线程U在MyClass
的不同实例上运行,则每个实例都有自己的lock
对象。通常,在这种情况下,同步块的主体仅对MyClass
的其他实例变量进行操作。由于两个线程在不同的数据上运行,因此它们同时在同一个同步块中没有任何害处。
同步的目的不是为了同时保持两个线程不在同一个方法中:目的是让两个线程不对相同的数据进行操作< / em>同时。
新手程序员有时写这个:
static Integer count = new Integer(0);
...
synchronized(count) {
count += 1;
...operate on static data...
}
这几乎总是一个错误。当线程T进入块时,它将在静态变量count
引用的Integer对象上同步。但接下来的事情是更新count
以引用不同的整数对象。然后是线程U.U在 new count
上同步,并且两个线程都在同一个块中,当它们不应该在相同的静态数据上运行时