显式锁
synchronized被称为内置锁,线程只要进入临界区就会自动获取锁,执行完同步代码后会自动释放锁。
J.U.C中的ReentrantLock、ReentrantReadWriteLock需要手动的进行加锁和解锁,与synchronized相呼应通常称之为显式锁。
ReentrantLock
顾名思义支持重入,和synchronized一样,线程可以重复获取同一个锁。同时ReentrantLock还支持公平性设定,可以设置其为公平锁或者是非公平锁。
public class ReentrantLock implements Lock, java.io.Serializable {
/** 队列同步器器 AQS实现 */
abstract static class Sync extends AbstractQueuedSynhronizer {... ...}
/** 非公平同步器 */
static final class NonfairSync extends Sync{... ...}
/** 公平同步器 */
static final class FairSync extends Sync{... ...}
/** 同步器实例 */
private final Sync sync;
/** 构造 */
public ReentrantLock() {
sync = new NonfairSync();
}
/** 构造 */
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
... ...
}
ReentrantLock使用队列同步器AQS作为基础同步工具,NonfairSync为非公平锁,FairSync为公平锁同步器,默认是非公平锁,可以通过fair设置为公平锁。
非公平锁加锁:
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {// 初次获取锁
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);// 设置持有线程为当前线程
return true;// 获取锁成功
}
} else if (current == getExclusiveOwnerThread()) {// 重入获取锁
int nextc = c + acquires;// 重入时直接将状态值+1
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
s1:State==0为首次获取锁,CAS将同步状态设置为1,设置成功说明获取到锁,将持有锁的线程设置为当前线程,返回。
s2:State>0并且持有线程为当前线程,将State设置为State+1,这里不存在线程争用,所以直接设置即可。
公平锁加锁方法:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {// 初次获取锁
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}// 重入获取锁
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;// 重入时直接将状态值+1
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
hasQueuedPredecessors()方法判断AQS同步队列中是否有等待的节点,如果有则放弃获取锁,这里就是体现公平锁FIFO特性的地方,让等待最久的节点优先获取锁。
解锁方法:
protected final boolean tryRelease(int releases) {
int c = getState() - releases;//释放时将状态值-1
//必须为持有线程才能释放锁
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) { //重入获取的锁 已释放完毕
free = true;
setExclusiveOwnerThread(null);
}
setState(c);// 不存在线程争用
return free;
}
非公平锁和公平锁的释放都调用了父类Sync的tryRelease方法。
如果锁完全释放即c == 0,会唤醒在同步队列中等待的后继节点,锁未完全释放不会唤醒后继节点。
于重入锁,加锁几次就必须要进行同样次数的释放锁,才能完成解锁。
小结
1:synchronized隐式的加锁解锁,ReentrantLock需要手动的加锁解锁
2:在同步代码中出现异常时,synchronized会自动释锁,ReentrantLock必须在finally中释放。
3:ReentrantLock可以非阻塞的获取锁(tryLock),synchronized不行。
4:ReentrantLock可以响应中断(lockInterruptibly),synchronized不行。
5:ReentrantLock具有超时机制(tryLock(timeout,unit)),synchronized不行。
6:ReentrantLock支持公平锁和非公锁的选择,而synchronized只能是非公平锁。
码字不易,转载请保留原文连接https://www.jianshu.com/p/79cbb3ab8af2