在java中,协调对共享对象的访问时可以使用的机制包括synchronized,volatile以及java5.0之后新增的ReentrantLock机制。需要注意的是,ReentrantLock不是替代内置锁的方法,而是当内置锁不适用时作为可选择的高级功能。

Lock提供了一种无条件的,可轮询的,定时的以及可中断的锁获取操作。Lock的实现中必须提供与内部所相同的内存可见性语义。ReentrantLock实现了Lock接口,其提供的互斥性和内存可见性与synchronized相同。

使用示例(引自java并发编程实战):

Lock lock=new ReentrantLock();
lock.lock();
try{
//更新对象状态
//捕获异常,并在必要时恢复不变性条件
}finally{
lock.unlock();
}

需要注意的是,unlock操作必须在finally中,否则相当于启动了"定时炸弹"。

如何选择

显示锁提供了额外的功能,包括定时,可中断,公平性以及非块结构加锁,此外性能上高于内置锁。

内置锁相对易于调试,通过线程转储可以给出哪些调用栈获得了哪些锁,并能检测和识别发生死锁的线程。它也是JVM的内置属性,在未来优化性能的空间更大。

接口代码

public interface Lock {
/*请求锁,实现时应该考虑能够检测出错误的使用,如避免死锁或者抛出未经检查的异常 */
void lock();
/*支持在请求锁的时候被中断 */
void lockInterruptibly() throws InterruptedException;
/*如果请求锁时锁可用则立即获取并返回true,否则立即返回false */
boolean tryLock();
/*如果请求锁时锁可用则立即获取并返回true ,否则线程进入不可调度状态直到以下三种情况:
*线程获取锁
*其他线程中断当前线程
*超时
*/
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
/*返回一个新的绑定到当前lock实例的Condition对象实例 */
Condition newCondition();
}
public interface Condition{
/**
*使当前线程等待直到被唤醒或者被中断。
*与该Condition关联的锁会被自动释放,然后线程进入休直到以下四种情况:
*1.其他线程调用Condition的signal方法且当前线程被选为被唤醒线程;
*2.其他线程调用signalAll方法;
*3.其他线程中断了当前线程;
*4.假唤醒。
*
*当前线程在方法返回之前必须重新请求锁
*/
void await() throws InterruptedException;
/*相对上一个方法,该方法不响应中断,直到被唤醒*/
void awaitUninterruptibly();
/*等待直到被唤醒,中断或者指定时间超时*/
long awaitNanos(long nanosTimeout) throws InterruptedException;
/*等同于awaitNanos(unit.toNanos(time)) > 0*/
boolean await(long time,TimeUnit unit) throws InterruptedException;
/*指定deadline*/
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();
}
Condition

在接口中出现了Condition类,在此简单的介绍一下条件队列:条件队列能使一组线程通过某种方式等待特定的条件为真,该队列中的元素是一个个正在等待相关条件的线程。

在java中,每个对象都可以作为一个条件队列,可以在Object对象中看到,每个对象都拥有wait(),notify()以及notifyAll()方法。而内部锁机制与内部条件队列是相互关联的,要调用某对象条件队列的方法时,必须持有该对象上的锁。只有能对状态进行检查时,才能在某个条件上等待,并且只有能修改状态时,才能从条件等待中释放一个线程。