1 概念
重入锁: 表示支持重新进入的锁,调用 lock 方 法获取了锁之后,再次调用 lock,是不会再阻塞,内部直接增加重入次数 就行了,标识这个线程已经重复获取一把锁而不需要等待锁的释放,比如递归调用
2 lock锁用法
public class LockTest {
static Lock lock = new ReentrantLock();
public static void method1(){
try{
lock.lock();
//进入method2方法
method2();
//可能会出现线程安全的操作
System.out.println("method1...");
}finally{
//尽量在finally中释放锁
lock.unlock();
}
}
public static void method2(){
try{
lock.lock();
//可能会出现线程安全的操作
System.out.println("method2...");
}finally{
//尽量在finally中释放锁
lock.unlock();
}
}
public static void main(String[] args) {
method1();
}
}
- lock()用来获取锁
- unlock()释放锁,最好在finally块中释放
- Lock 是 java.util.concurrent.locks.lock 包下的,是 api层面的锁
3 实现原理
ReentrantLock主要利用CAS+AQS队列来实现。它支持公平锁和非公平锁,两者的实现类似
构造方法接受一个可选的公平参数(默认非公平锁),当设置为true时,表示公平锁,否则为非公平锁。公平锁的效率往往没有非公平锁的效率高,在许多线程访问的情况下,公平锁表现出较低的吞吐量。
查看ReentrantLock源码中的构造方法:
public ReentrantLock() {
//非公平锁
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
//公平锁
sync = fair ? new FairSync() : new NonfairSync();
}
加锁解锁流程(非公平)
先从构造器开始看,默认为非公平锁实现
public ReentrantLock() {
//非公平锁
sync = new NonfairSync();
}
NonfairSync继承自 AQS,也就说底层主要实现也是基于AQS
非公平锁体现
当某一个线程释放锁之后,会从AQS维护的双向队列中激活一个线程去抢锁(下图中的Thread-1),这个时候同时有一个新的线程(下图中的Thread-4)抢锁,如果不巧又被 Thread-4 占了先
- Thread-4 被设置为 exclusiveOwnerThread(锁拥有者),state = 1
- Thread-1 再次进入 acquireQueued 流程,获取锁失败,重新进入 park 阻塞
公平锁体现
上图的情况就变为,Thread-4会乖乖去队列中等待被激活。Thread-1会抢到锁