锁和条件的要点总结:
- 锁用来保护代码片段,一次只能由一个线程执行被保护的代码。
- 锁可以管理试图进入被保护代码段的线程。
- 一个锁可以有一个或多个相关联的条件对象。
- 每个条件对象管理那些已经进入被保护代码段但还不能运行的线程。
Lock和Condition接口允许程序员充分控制锁定。不过,大多数情况下,并不需要那样控制,完全可以使用Java语言内置的一种机制。从1.0版开始,Java中的每个对象都有一个内部锁。如果一个方法声明时有synchronized关键字,那么对象的锁将保护整个方法。也就是说,线程必须获得内部对象锁。
换句话说:
public synchronized void method(){
method body
}
等价于
public void method{
this.intrinsicLock.lock();
try {
method body
}
finally { this.intrinsicLock.unlock(); }
}
内部对象锁只有一个关联条件。wait方法将一个线程增加到等待集中,notifyAll/notify方法可以解除等待线程的阻塞。换句话说,调用wait或notifyAll等价于
intrinsicCondition.await();
intrinsicCondition.signalAll();
每个对象都有一个内部锁,并且这个锁有一个内部条件。这个锁会管理试图进入synchronized方法的线程,这个条件可以管理调用了wait的线程。
将静态方法声明为同步也是合法的。如果调用这样一个方法,它会获得相关类对象的内部锁。例如,如果Bank类有一个静态同步方法,那么当调用这个方法时,Bank.class对象的锁会锁定。因此,没有其他线程可以调用这个类的该方法或任何其他同步静态方法。
内部锁和条件存在一些限制。包括:
- 不能中断一个正在尝试获得锁的线程。
- 不能指定尝试获得锁的超时时间。
- 每个锁仅有一个条件可能是不够的。
在代码中应该使用哪一种做法呢?Lock和Condition对象还是同步方法? - 最好既不使用Lock/Condition也不使用synchronized关键字。在许多情况下,可以使用java.util.concurrent包中的某些机制,它会为你处理所有的锁定。
- 如果synchronized关键字适合你的程序,那么尽量使用这种做法,这样可以减少编写的代码量,还能减少出错的概率。
- 如果特别需要Lock/Condition结构提供的额外能力,则使用Lock/Condition。
同步块
线程可以通过调用同步方法获得锁。还有另一种机制可以获得锁:即进入一个同步块。当线程进入如下形式的块时:
private Object obj = new Object();
...
synchronized (obj){
// work code
}
它会获得obj的锁。
在这里,创建obj对象只是为了使用每个Java对象拥有的锁。