ReentrantLock的使用
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockDemo {
// private static Lock lock =new ReentrantLock(false);
private static Lock lock =new ReentrantLock(true);
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
new Thread(new ThreadDemo(i)).start();
}
}
static class ThreadDemo implements Runnable{
Integer id;
public ThreadDemo(Integer id){
this.id = id;
}
@Override
public void run() {
try{
TimeUnit.MICROSECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < 2; i++) {
lock.lock();
System.out.println("获得锁的线程:" + id);
lock.unlock();
}
}
}
}
Lock接口源码解析
ReentrantLock实现了Lock接口:
public interface Lock {
//加锁
void lock();
//加锁并响应中断
void lockInterruptibly() throws InterruptedException;
//只有空闲的才能获取锁,返回boolean值
boolean tryLock();
//在指定时间内获取锁,可以响应中断
boolean tryLock(long var1, TimeUnit var3) throws InterruptedException;
//解锁
void unlock();
//返回Condition对象
Condition newCondition();
}
ReentrantLock内部类源码解析
说明:ReentrantLock类内部共存在Sync、NonfairSync、FairSync三个内部类,NonfairSync和FairSync继承自Sync,Sync继承自AbstractQueuedSynchronizer抽象类。
内部类Sync类源码解析
/**
*重入锁基础的同步控制器
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
//加锁方法,由子类FairSync、NonfairSync实现
abstract void lock();
//非公平方式加锁
@ReservedStackAccess
final boolean nonfairTryAcquire(int acquires) {
//当前线程
Thread current = Thread.currentThread();
//获取锁的状态,即AQS中的state状态
int c = this.getState();
//c == 0表示没有线程持有该锁
if (c == 0) {
//CAS修改state状态,如果CAS成功,即加锁成功
if (this.compareAndSetState(0, acquires)) {
//设置当前线程以独占的方式持有锁
this.setExclusiveOwnerThread(current);
//返回加锁成功
return true;
}
//如果c不等于0,表示锁此时被占着
//如果当前线程就是锁的持有者
} else if (current == this.getExclusiveOwnerThread()) {
//发生重进入,增加重进入的次数
int nextc = c + acquires;
//如果nextc小于0即发生了int类型的溢出
if (nextc < 0) {
throw new Error("Maximum lock count exceeded");
}
//设置state状态
this.setState(nextc);
//返回重入加锁成功
return true;
}
//返回加锁失败
return false;
}
//对类AQS的tryRelease()做重写
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
//获取state状态,减去要释放的资源数release
int c = this.getState() - releases;
//如果当前线程不是锁的拥有者
if (Thread.currentThread() != this.getExclusiveOwnerThread()) {
//抛出异常
throw new IllegalMonitorStateException();
} else {
//是否完全释放锁
boolean free = false;
//如果c等于0
if (c == 0) {
//表示锁已经完全释放,没有任何线程持有锁
free = true;
//将锁的持有者置为null
this.setExclusiveOwnerThread((Thread)null);
}
//设置state状态
this.setState(c);
//返回锁是否完全被释放的状态
return free;
}
}
//对AQS中的isHeldExclusively做重写,检验当前线程是否为锁的拥有者
protected final boolean isHeldExclusively() {
return this.getExclusiveOwnerThread() == Thread.currentThread();
}
//返回一个AQS内部类ConditionObject对象
final ConditionObject newCondition() {
return new ConditionObject(this);
}
//返回锁的持有者
final Thread getOwner() {
//如果state为0,则未加锁,返回null
//否则调用getExclusiveOwnerThread方法获取持有者
return this.getState() == 0 ? null : this.getExclusiveOwnerThread();
}
//返回重入锁重进入次数
final int getHoldCount() {
//如果isHeldExclusively为true
//则说明当前线程是锁的持有者,返回state
//否则返回0
return this.isHeldExclusively() ? this.getState() : 0;
}
//判断是否已经被加锁
final boolean isLocked() {
return this.getState() != 0;
}
//通过流反序列化对象
private void readObject(ObjectInputStream s) throws IOException, ClassNotFoundException {
s.defaultReadObject();
this.setState(0);
}
}
内部类FairSync源码解析
/**
*FairSync继承Sync 实现公平锁
*/
static final class FairSync extends ReentrantLock.Sync {
private static final long serialVersionUID = -3000897897090466540L;
/**
*加锁
*/
final void lock() {
acquire(1);
}
/**
*父类acquire()方法是个模板方法
*需要调用子类的tryAcquire()实现
*/
@ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
//当前线程
Thread current = Thread.currentThread();
//锁状态
int c = this.getState();
//c等于0表示未被锁定
if (c == 0) {
//hasQueuedPredecessors()是父类AQS中的方法
//用于查询是否有等待获取锁的时间比当前线程更长的线程
//如果没有比当前线程等待更久的线程
//则通过CAS设置state状态从0变成acquires
//如果CAS成功,说明线程加锁成功
//设置锁的持有者为当前线程
if (!this.hasQueuedPredecessors() && this.compareAndSetState(0, acquires)) {
this.setExclusiveOwnerThread(current);
//返回加锁成功
return true;
}
//如果c不等于0
//判断当前线程是不是锁的拥有者
//如果是则发生重入
} else if (current == this.getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) {
throw new Error("Maximum lock count exceeded");
}
this.setState(nextc);
return true;
}
return false;
}
}
ReentrantLock属性源码解析
ReentrantLock只有一个属性,就是内部类Sync
private final Sync sync;
ReentrantLock构造器源码解析
/**
* 默认是非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* 通过参数执行公平/非公平锁
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReentrantLock公平锁加锁/解锁源码解析
public void lock() {
sync.lock();
}
ReentrantLock的lock()方法调用Sync的lock()方法。sync是ReentrantLock的属性,从ReentrantLock的构造器可以看出,sync可能是公平锁或非公平锁。
ReentrantLock公平锁加锁源码解析
进入FairSync的lock()方法
public void lock() {
sync.lock();
}
调用父类AQS的acquire()方法
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
父类AQS的acquire()方法调用tryAcquire()方法,由子类实现
protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}
tryAcquire()方法在FairSync的实现如下:
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;
}
如果tryAcquire()方法返回false,则加锁失败,回到父类AQS的acquire()方法接着执行下面代码
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
先执行addWaiter()方法:
private Node addWaiter(Node mode) {
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
}
enq(node);
return node;
}
首先以当前线程创建了一个新的节点,将节点尝试通过CAS操作加入到同步队列的末尾,如果CAS失败,则存在竞争队尾的情况,调用enq()方法进行自旋:
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
回到父类AQS的acquire()方法,addWaiter()执行结束,将会执行acquireQueued():
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取当前节点的前驱节点
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
//设置当前节点为头节点
setHead(node);
p.next = null; // help GC
failed = false;
//正常情况下循环唯一的出口
return interrupted;
}
//判断是否要阻塞当前线程
if (shouldParkAfterFailedAcquire(p, node) &&
//阻塞当前线程
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
当前线程首先判断前驱节点是否是头节点。如果是则尝试获取锁,获取锁成功则会设置当前节点为头节点。如果当前节点的前驱节点不是头节点,或者尝试加锁失败,调用shouldParkAfterFailedAcquire()方法,确保前驱节点为SIGNAL,然后阻塞当前线程:
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
return true;
if (ws > 0) {
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
根据前驱节点的不同状态进行不同操作:
- pred的状态为SIGNAL,直接返回true,表示要阻塞当前线程
- pred的状态为CANCELLED,则一直向前回溯直到找到第一个状态不为CANCELLED的节点,将当前节点node挂在这个节点后面
- pred的状态为初始化状态,通过CAS操作,把pred的状态改为SIGNAL
其实这个方法很简单,就是确保了前驱节点为SIGNAL状态,只有这样才意味着线程释放锁后会唤醒后面的阻塞线程,只有在确保了会被唤醒的情况下,当前线程才能放心被阻塞。
shouldParkAfterFailedAcquire返回true,应当阻塞当前线程,则接着执行方法parkAndCheckInterrupt(),此方法调用了LockSupport来阻塞线程
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
ReentrantLock公平锁解锁源码解析
ReentrantLock解锁源码如下:
public void unlock() {
sync.release(1);
}
调用父类AQS的release()方法:
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
tryRelease()方法由子类实现:
protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}
tryRelease()方法在ReentrantLock中的Sync中实现:
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
tryRelease()方法返回true,即解锁成功。将会执行unparkSuccessor()方法唤醒后继节点:
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
ReentrantLock非公平锁加锁/解锁源码解析
ReentrantLock非公平锁加锁源码解析
非公平锁加锁入口与公平锁加锁一样,都是lock()方法,只是调用的sync对象的引用不同:
public void lock() {
sync.lock();
}
NonfairSync中lock()方法的实现如下:
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
与公平锁FairSync中的lock()方法不同,NonfairSync中对lock()方法直接进行CAS操作。成功则设置当前线程为锁的持有者。CAS操作失败,说明存在竞争,进入父类AQS的acquire()方法:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
tryAcquire()方法依旧需要子类实现。NonfairSync中的实现如下:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
NonfairSync中tryAcquire()方法调用的是nonfairTryAcquire()是父类Snyc的方法:
final boolean nonfairTryAcquire(int acquires) {
//当前线程
Thread current = Thread.currentThread();
//获取锁的状态,即AQS中的state状态
int c = this.getState();
//c == 0表示没有线程持有该锁
if (c == 0) {
//CAS修改state状态,如果CAS成功,即加锁成功
if (this.compareAndSetState(0, acquires)) {
//设置当前线程以独占的方式持有锁
this.setExclusiveOwnerThread(current);
//返回加锁成功
return true;
}
//如果c不等于0,表示锁此时被占着
//如果当前线程就是锁的持有者
} else if (current == this.getExclusiveOwnerThread()) {
//发生重进入,增加重进入的次数
int nextc = c + acquires;
//如果nextc小于0即发生了int类型的溢出
if (nextc < 0) {
throw new Error("Maximum lock count exceeded");
}
//设置state状态
this.setState(nextc);
//返回重入加锁成功
return true;
}
//返回加锁失败
return false;
}
与非公平锁加锁不同,公平锁加锁调用了hasQueuedPredecessors()方法,判断等待队列中是否存在比当前线程先入队的线程。公平锁需要对先入队的线程进行让步,先来后到。非公平锁不管先来后到,直接加锁。
tryAcquire()方法返回false,则加锁失败。将继续执行:
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
随后的流程跟公平锁一致。
ReentrantLock非公平锁解锁源码解析
ReentrantLock非公平锁解锁与公平锁解锁过程一样。
公平锁FairSync VS 非公平锁NonfairSync
两者没有绝对的好坏之分,根据具体的场景选择对应的锁技术才是好的。
- 公平锁——侧重公平性,先来后到。
- 非公平锁——侧重并发性,调用即加锁。
非公平锁对锁的竞争是抢占式的(队列中的线程除外),线程在进入等待队列前可以进行两次尝试,很大程度上增加了获取到锁的机会。优点在于:
- 线程不必加入等待队列就可以获得锁,不仅免去了构建节点并加入队列的繁琐操作,同时也节省了线程阻塞唤醒的开销。线程阻塞和唤醒涉及到线程上下文的切换和操作系统的系统调用,是非常耗时的。在高并发情况下,如果线程持有锁的时间非常短,短到线程入队列阻塞的过程超过线程持有锁并释放锁的时间开销,那么这种抢占式的特性对并发性能的提升会更加明显
- 减少CAS竞争。如果线程必须要加入阻塞队列才能获取到锁,那入队时CAS竞争将变得异常激烈,CAS操作虽然不会导致失败的线程挂起,但是不断失败重试导致对CPU的浪费也不能忽视。