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
类似,可重入,支持公平锁和非公平锁,不同的时,它支持读写锁。