可重入互斥锁ReentrantLock
ReentrantLock是Java并发包中可重入互斥锁,其基本行为和语义与使用synchronized方法和语句访问的隐式监视锁相同,但扩展性更强。
一个ReentrantLock锁由上次成功锁定但尚未解除锁定的线程拥有。当锁不属于另一个线程时,调用lock()的线程将返回并成功获取锁。如果当前线程已经拥有锁,则方法将立即返回。这可以使用方法isheldbycurrenthread和getHoldCount来检查。
除了实现Lock接口之外,这个类还定义了一些方法来检查锁的状态。此锁支持同一线程最多2147483647个递归锁。尝试超过此限制将导致锁定方法抛出Error。
类定义
public class ReentrantLock
implements Lock, java.io.Serializable
Lock实现类提供了比使用synchronized方法和语句更广泛的锁定操作。它们允许更灵活的结构,可能具有完全不同的属性,并且可能支持多个关联的Condition对象。
构造方法
/**
* 默认创建非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* 使用给定的公平性策略创建ReentrantLock实例。
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即FIFO先进先出顺序。非公平锁就是一种获取锁的抢占机制,是随机获得锁的,先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果也就是不公平的了。当然这里的顺序并不是调用线程start()方法的顺序。
基本使用格式
ReentrantLock lock = new ReentrantLock();
// 获取锁
lock.lock();
try {
// 业务逻辑
} finally {
// 释放锁
lock.unlock();
}
ReentrantLock的锁功能主要是通过继承了AbstractQueuedSynchronizer的内部类Sync来实现的,AbstractQueuedSynchronizer(AQS)是一种提供了原子式管理同步状态、阻塞和唤醒线程功能以及队列模型的简单框架。
基本应用
从运行结果看,当前线程打印完毕之后释放锁之后,其他线程才可以打印,线程打印的数据是分组打印的,因为当前线程已经持有锁,但线程之间的打印顺序还是随机的。
Condition
在使用notify()/notifyAll()方法进行通知时,被通知的线程是由JVM随机选择的。但使用ReentrantLock结合Condition类是可以实现选择性通知的,而synchronized就相当于整个Lock对象中只有一个单一的Condition对象,所有的线程都注册在它一个对象的身上。线程开始notifyAll()时,需要通知所有的waiting线程,没有选择权,会出现相当大的效率问题。
public interface Condition
接口中的方法也比较简单就2个功能:等待和唤醒。
ReentrantLock中调用newCondition()返回的是AbstractQueuedSynchronizer中的ConditionObject。
多个Condition实现部分通知