JDK1.5之后增加了java.util.concurrent.locks,提供了与内建锁完全不同的实现多线程共享资源访问的机制。失去了内建锁隐式的加锁与解锁过程,增加了可中断的获取锁、超时获取锁以及共享锁等内建锁不具备的特性。
Lock锁的标准使用形式
Lock lock = new ReentrantLock();
try{
lock.lock();
}catch{}
finally{
lock.unlock();
}//显式上锁与解锁
需要注意的是synchronized同步块执行完成或者遇到异常是锁会自动释放,而lock必须调用unlock()方法释放锁,因此在finally块中释放锁,无论程序是否有异常,都有保证锁要释放。
lock接口中定义了下面这些方法:
Lock接口的实现子类
基 本 上 所 有 的 方 法 的 实 现 实 际 上 都 是 调 用 了 其 静 态 内 存 类 Sync 中 的 方 法 , 而 Sync 类 继 承 了
AbstractQueuedSynchronizer ( AQS ) 。 可 以 看 出 要 想 理 解 ReentrantLock 关 键 核 心 在 于 对 队 列 同 步 器
AbstractQueuedSynchronizer(简称同步器)的理解。
AQS同步器是lock体系最核心的存在.
同步器是用来构建锁与其他同步组件的基础框架。它的实现主要是依赖一个int成员变量来表示同步状态以及通过一个FIFO队列构成同步队列。
要使用AQS,推荐使用静态内部类继承AQS,重写AQS中的protected用来改变同步状态的方法,其他方法主要是实现排队与阻塞机制。状态更新使用getState(),setState()、compareAndSetState()
同步器是实现锁(也可以是任意同步组件)的关键,在锁的实现中聚合同步器,利用同步器实现锁的语义。可以这样理解二者的关系:Lock面向使用者,定义了使用者与锁交互的接口,隐藏了实现细节;AQS面向锁的实现者,简化了锁的实现方式,屏蔽同步状态的管理、线程排队、线程等待与唤醒等底层操作。
AQS的设计是使用模板方法设计模式,它将一些方法开放给子类进行重写,而同步器给同步组件所提供模板方法又
会重新调用被子类所重写的方法。
举个例子,AQS中需要重写的方法tryAcquire:
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
ReentrantLock中NonfairSync(继承AQS)会重写该方法为:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
而AQS中的模板方法acquire():
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
会调用tryAcquire方法,而此时当继承AQS的NonfairSync调用模板方法acquire时就会调用已经被NonfairSync重写的
tryAcquire方法。这就是使用AQS的方式
下一篇文章会自己实现一个简易的Lock锁.