我是线程技术的新手,我正在尝试采用混合方法。我有下面的代码。
if(lock.tryLock())
{
try
{
//do some actions
lock.notifyAll(); // error throwing line
}
finally
{
lock.unlock();
}
}
如果我像这样运行程序,则会在该错误引发行引发非法监视器异常。但是,如果我在下面的同步块中调用,它将起作用。
if(lock.tryLock())
{
try
{
//do some actions
synchronized ( lock )
{
lock.notifyAll();
}
}
finally
{
lock.unlock();
}
}
我的问题是因为我将tryLock设置为true,这是否意味着我已经获得了锁并且可以安全地调用wait()和notify()方法? 在此先感谢。
答案 0 :(得分:3)
在这里忘记“混合方法”,这是行不通的。
每个对象都有一个隐式锁。其中包括诸如ReentrantLock之类的Lock类的对象。调用wait and notify始终使用隐式锁,这些方法不使用您要对其进行调用的Lock对象的锁定功能。在对象中,wait,notify和notifyAll方法被声明为本地方法和最终方法。
要等待并通知工作,您必须在锁对象上进行同步,并且通过诸如tryLock之类的方法进行的锁定将是无关紧要的,最终在功能上等同于final Object lock = new Object();
,这更加令人困惑。
锁对象具有它们自己的等效项,如果您使用的是java.util.concurrent.locks.Lock
,则从锁中获取条件,然后调用await(等效于wait)和signal / signalAll(等效于notify / notifyAll) )。
使用Lock对象,您可以具有多个条件,从而使您可以发出等待锁定的线程子集的信号。因此,您几乎不需要隐式锁定代码需要notifyAll的任何地方的signalAll。
例如,如果您查看ArrayBlockingQueue的实现方式,它使用ReentrantLock,那么对于消费者来说有一个条件,对于生产者来说有另一个条件:
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
由
构建public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
使用隐式锁的等效代码必须调用notifyAll以避免丢失通知,因为我们不知道被通知的线程是生产者还是使用者,但是在单独的条件下,我们知道将通知哪种线程。例如,出队代码在notFull条件下调用信号,最多唤醒一个线程:
/**
* Extracts element at current take position, advances, and signals.
* Call only when holding lock.
*/
private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();
return x;
}