ReentrantLock即可重入锁(当前线程获取该锁再次获取不会被阻塞),是一种递归无阻塞的同步机制。ReentrantLock基于AQS来实现,相对于内置锁synchronized关键字功能更强大,多了等待可中断、公平性、绑定多个条件等机制,还能够tryLock()避免死锁,而若单独从性能角度出发,更推荐synchronizedjava
lock方法:编程
public void lock() {
sync.lock();
}
复制代码
Sync为ReentrantLock里面的一个内部类,它继承AQS,它有两个子类:公平锁FairSync和非公平锁NonfairSync,ReentrantLock里面大部分的功能都是委托给Sync来实现的,以非公平锁为例其lock()方法并发
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
复制代码
若锁未线程占有,把同步器中的exclusiveOwnerThread设置为当前线程
若锁已有线程占有,nonfairTryAcquire方法中,会再次尝试获取锁,在这段时间若是该锁被成功释放,就能够直接获取锁而不用挂起,其完整流程:ide
公平锁与非公平锁的区别在于获取锁的时候是否按照FIFO的顺序。
性能
ReentrantLock默认采用非公平锁(组合方式)ui
public ReentrantLock() {
sync = new NonfairSync();
}
复制代码
实现非公平锁的核心方法nonfairTryAcquire(),其源码以下:spa
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取同步状态
int c = getState();
// 若同步状态为0,代表该锁未被任何线程占有
if (c == 0) {
// CAS设置同步状态
if (compareAndSetState(0, acquires)) {
// 设置锁的拥有线程
setExclusiveOwnerThread(current);
return true;
}
}
// 检查占有线程是不是当前线程,可重入性关键代码
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
复制代码
其主要逻辑:判断同步状态是否为0,若为0代表该锁未被任何线程占有,CAS设置同步状态;若不为0代表该锁已被线程占有,判断锁占有线程是不是当前线程,如果增长同步状态(可重入性机制实现的关键)
线程
公平锁,经过ReentrantLock有参构造方法传入truecode
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
复制代码
实现公平锁的核心方法tryAcquire(),其源码以下:cdn
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;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
复制代码
能够很明显地发现与nonfairTryAcquire()方法惟一的区别在于CAS设置尝试设置state值以前,调用了hasQueuedPredecessors()判断当前线程是否位于CLH同步队列中的第一个,若不是先执行完同步队列中结点的线程,当前线程进入等待状态
public final boolean hasQueuedPredecessors() {
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
复制代码
可重入性须要解决如下两个问题:
①.线程再次获取锁:锁须要去识别获取锁的线程是否为当前占据锁的线程,若是是则再次成功获取 次成功获取
②.锁的最终释放:线程重复n次获取了锁,只有在n次释放该锁后,其余线程才能获取到该锁
在nonfairTryAcquire()、tryAcquire()方法中都有这段代码:
if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
复制代码
为了支持可重入性,若同步状态不为0时,还会再判断锁持有线程是不是当前请求线程,如果再次获取该锁,同步状态加1。再来看看释放锁:
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
// 同步状态为0时,锁才能释放,将其持有线程置为null
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
复制代码
只有同步状态彻底释放了,才能返回true。能够看到,该方法将同步状态是否为0做为最终释放的条件,当同步状态为0时,将占有线程设置为null,并返回true,表示释放成功。
每个Lock能够有任意数据的Condition对象,Condition是与Lock绑定的。Condition接口定义的方法,await对应于Object.wait,signal对应于Object.notify,signalAll对应于Object.notifyAll。
生产者消费者简单demo:
public class Resource {
private int num = 1;//当前数量
private int maxNum = 10;//极值
private Lock lock = new ReentrantLock();
private Condition productCon = lock.newCondition();
private Condition consumerCon = lock.newCondition();
public void product() {
lock.lock();
try {
while (num >= maxNum) {
try {
System.out.println("当前已满");
productCon.await();
} catch (InterruptedException e) {
}
}
num++;
System.out.println("生产者" + Thread.currentThread().getName() + "当前有" + num + "个");
consumerCon.signal();
} finally {
lock.unlock();
}
}
public void consume() {
lock.lock();
try {
while (num == 0) {
try {
System.out.println("当前已空");
consumerCon.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
num--;
System.out.println("消费者" + Thread.currentThread().getName() + "当前有" + num + "个");
productCon.signal();
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
final Resource r = new Resource();
// 生产者
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
r.product();
}
}
}).start();
// 消费者
new Thread(new Runnable() {
@Override
public void run() {
while (true) {
r.consume();
}
}
}).start();
}
}
复制代码
《java并发编程的艺术》 https://www.jianshu.com/p/4358b1466ec9