Lock 接口和类
Java 原生的锁-基于对象的锁,一般配合 synchronized 关键字使用。 Java 在 java.util.concurrent.locks 包下,提供了几个关于锁的接口和类,他们有更强大的功能和性能
synchronized 的不足
- 临界区是只读操作,可以多线程一起执行,
synchronized只能有一个线程执行 synchronized无法直到当前线程有没有成功获取到锁- 使用
synchronized, 如果临界区因为IO或者sleep等阻塞了,当前线程又未释放锁,会导致所有线程等待。
这些问题都是locks包下的锁可以解决的
锁的分类
是否可重入
重入锁,就是说支持一个线程对资源重复加锁。
synchronized 使用的重入锁,就是 synchronized实例方法里可以调用另一个本实例的synchronized实例方法。
ReentrantLock 也是重入锁,是本文介绍的重点类。
是否公平
公平,就是FIFO,是否是先来后到的顺序来获取锁。
非公平锁是一种抢占机制,随机获得锁。
一般情况下,非公平锁会提升一定的效率。
非公平说可能会发生线程饥饿。
读写锁和排他锁
synchronized用的锁和ReentrantLock 都是排他锁,即这些锁只能有一个线程访问。
读写锁可以运行多个线程同时访问。Java提供了 ReentrantReadWriteLock 作为读写锁的默认实现,内部维护了两个锁:读锁和写锁。
读写锁,在写线程访问时,所有的读线程和其它写线程均被阻塞。
JDK 有关锁的接口
抽象类 AQS/AQLS/AOS
AQS 是抽象队列同步器,资源使用 int类型的 state 来表示, AQLS (AbstractQueuedLongSynchronizer)代码和AQS 几乎一样,资源类型变成了 long
AQS 和 AQLS 继承了 AOS(AbstractOwnableSynchronizer).这个类用来表示锁和持有者的关系(独占模式)
// 独占模式,锁的持有者
private transient Thread exclusiveOwnerThread;
// 设置锁持有者
protected final void setExclusiveOwnerThread(Thread t) {
exclusiveOwnerThread = t;
}
// 获取锁的持有线程
protected final Thread getExclusiveOwnerThread() {
return exclusiveOwnerThread;
}
接口 Condition/Lock/ReadWriteLock
Lock 里有释放和获取锁的方法声明,Lock 可以获得 Condition
Condition newCondition();
ReadWriteLock 里有两个方法, 分别返回 读锁 和 写锁
public interface ReadWriteLock {
/**
* Returns the lock used for reading. * * @return the lock used for reading
*/
Lock readLock();
/**
* Returns the lock used for writing. * * @return the lock used for writing
*/
Lock writeLock();
}
对象锁是继承Object的wait/notify方法实现 等待/通知 机制。Condition接口提供了类似的方法,通过Lock来实现 通知/等待 机制。 我们来做一下总结
| 项目 | 对象锁 | Condition |
|---|---|---|
| 前置条件 | synchronized 获取对象锁 | 通过Lock.lock 获取锁,调用Lock.newCondition 获取 Condition 对象 |
| 调用方式 | object.wait/notify() | condition.await/signal() |
| 等待队列个数 | 1个 | 多个 |
| 释放锁进入 等待/超时等待 模式 | 支持 | 支持 |
| 释放锁进入等待,状态不中断 | 不支持 | 支持 |
| 唤醒队列中一个/全部线程 | 支持 | 支持 |
Condition 和 Object 的 wait / notify 类似。
Condition 中的 await 对用 Object 的wait,signal/signal 对应 Object 的 notify/notifyuAll().
ReentrantLock
JDK 中 Lock 的默认实现,实现了锁的基本功能。内部有一个抽象类 Sync,Sync继承了 AQS。
ReentrantLock内部有两个非抽象类NonfairSync和FairSync,它们都继承了Sync。这意味着 ReentrantLock 可以支持公平锁和非公平锁。
在ReentrantLock的构造方法里,可以传入一个
boolean类型的参数,来指定它是否是一个公平锁,默认情况下是非公平的。这个参数一旦实例化后就不能修改,只能通过isFair()方法来查看。
ReentrantReadWriteLock
JDK 中 ReadWriteLock 的默认实现,与ReentrantLock 类似,可重入,支持公平锁和非公平锁,不同的时,它支持读写锁。
