首先看下ReentrantLock的构造方法
/**
* 无参构造方法默认为非公平锁
*/
public ReentrantLock() {
sync = new NonfairSync();
}
/**
* 有参构造方法,true为公平锁,false为非公平锁
*/
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
公平锁和非公平锁有什么区别
/**
* 公平锁加锁
*/
final void lock() {
acquire(1);
}
/**
* 非公平锁加锁
*/
final void lock() {
//线程入队前争夺资源
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
这地方解释下,ReentrantLock是内部维护了一个AbstractQueuedSynchronizer抽象队列同步器简称AQS,
内部维护了线程入队出队的规则
private transient volatile Node head; //队列头节点
private transient volatile Node tail; //队列尾节点
private volatile int state; //0表示没有线程持有锁,1表示当前有线程持有锁,>1表示重入锁
//维护的节点结构
static final class Node {
volatile Node prev;//当前节点前驱
volatile Node next;//当前节点后继
volatile Thread thread;//节点存储的线程信息
}
从代码和多线程竞争图示可看出:公平锁(线程直接入队),非公平锁(线程入队之前会尝试持有锁,失败排队),入队后就是一日排队终身排队,不能插队竞争
package com.hx.zbhuang.mianEntrance;
import java.util.concurrent.locks.ReentrantLock;
public class LockExplore {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock(true);
Thread t1 = new Thread("t1"){
@Override
public void run() {
lock.lock();
System.out.println("aaa");
lock.unlock();
}
};
t1.start();
Thread t2 = new Thread("t1"){
@Override
public void run() {
lock.lock();
System.out.println("aaa");
lock.unlock();
}
};
t2.start();
Thread t3 = new Thread("t1"){
@Override
public void run() {
lock.lock();
System.out.println("aaa");
lock.unlock();
}
};
t3.start();
}
}
查看加锁过程:
1.首先调用tryAcquire方法,单线程,多线程交替执行,重入锁时不需要入队。
/**
* 加锁
* @arg 每次加锁锁标识state+arg(数值为1)
*/
public final void acquire(int arg) {
//首先查看是否已经存在线程竞争,如果是单线程或者交替执行,直接设置线程持有锁,无需创建队列
//如果是线程再次加锁(重入锁),直接将锁状态标识+args(1),重置锁状态标识
if (!tryAcquire(arg) &&
//如果存在线程竞争,先初始化队列头结点,将当前线程封装成node节点入队
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
//防止线程竞争锁资源时被interrupt
selfInterrupt();
}
/**
* 尝试获取锁,如果返回false则需要入队,返回true当前线程持有锁
* @acquires每次加锁锁标识state+acquires(数值为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()) {
//将锁状态标识+acquires(默认为1)
int nextc = c + acquires;
//可能是线程t1未加锁直接解锁还没抛出异常时线程二执行加锁出现异常
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
//更新重入后锁状态标识
setState(nextc);
return true;
}
return false;
}
}
/**
* 判断是否需要入队
* 从上面方法可以看出,当前方法返回false,直接去持有锁,返回true的时候需要判断是否存在线程竞争或者
* 是否为重入锁,1.如果为单线程或者多线程交替执行或者为重入锁的时候,不存在锁竞争,因为没有还没有
* 初始化AQS队列,这时候tail=head=null,这时候h!=t为false,方法直接返回false,直接持有锁,2.如果是
* 存在竞争,多个线程排队依次执行,直到最后一个线程获取锁的时候,既只剩下头结点(null节点),这时候
* 新线程来的时候head=new Node(null),tail=null,h!=t,(s = h.next) == null为方法返回true,进行排
* 队,如果持有锁线程执行完,刚排队的线程再次进入,head=tail=null时候,h!=t方法返回false直接持有
* 锁,3.如果存在竞争,多个线程在排队,这时候来个新线程,这时候head=new Node(null);tail=new
* Node(xxx),h!=t,(s = h.next) != null,不为持有锁的线程s.thread != Thread.currentThread()为
* true,方法返回true,进行排队,4.如果存在竞争,线程在排队,如第三点,但是这是来的线程是持有锁的
* 线程,s.thread != Thread.currentThread()为false,当前线程重入锁,在tryAcquire将锁状态标识
* state+acquires(默认入参为1)
*/
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
2.存在竞争情况下需要先初始化队列调用addWaiter(Node.EXCLUSIVE)
/**
* 根据当前队列是否初始化,如果队列初始化需要先初始化一个内容为null的头结点标识当前持有锁线程,然
* 后在header节点之后添加排队的节点,主要存储排队的线程信息
* @node 当前线程后继节点
*/
private Node addWaiter(Node mode) {
//将当前线程封装成node节点
Node node = new Node(Thread.currentThread(), mode);
//获取队列的尾节点,注意如果队列初始化过tail节点一定存在,至少为new Node()空节点不为null
Node pred = tail;
//如果尾节点不为空(队列初始化过)
if (pred != null) {
//将当前线程的前驱指向尾节点
node.prev = pred;
//将当前节点设置为队列的尾节点
if (compareAndSetTail(pred, node)) {
//将上一尾节点后继指向当前线程,当前线程入队
pred.next = node;
return node;
}
}
//队列未初始化则初始化队列
enq(node);
return node;
}
/**
* 初始化AQS队列
* 首先判断队列是否为空,队列未初始化的时候第一次循环添加new Node()内容为null的头结点
* 第二次循环将当前线程添加为尾节点
* @node当前线程封装的节点
*/
private Node enq(final Node node) {
for (;;) {
Node t = tail;
//判断尾节点是否为空
if (t == null) {
//如果尾节点为null,队列未初始化,先添加一个空的header节点标识持有锁线程
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
// header节点添加后将当前线程添加到队列尾部
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
3.入队
/**
* @node当前线程封装的节点
* @arg 每次加锁锁标识state+arg(数值为1)
*/
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)) {
//加锁成功将持有锁线程设置为head节点断开和上一head节点的联系
setHead(node);
//断开上一head节点所有连接
p.next = null; // help GC
//加锁状态置为成功
failed = false;
//返回加锁过程是否被interrupted标识
return interrupted;
}
//判断当前线程前一节点的等待状态,默认初始状态为0,第一次循环进入
//shouldParkAfterFailedAcquire方法会将状态置为-1,第二次进入返回true
//判断是否被interrupted ,如果被中断修改中断状态标识
if (shouldParkAfterFailedAcquire(p, node) &&
//将上一线程的waitstatu置为-1后当前线程park,并且检查加锁过程中当前是否被中断
parkAndCheckInterrupt())
//如果当前线程被中断,则设置中断标识为true
interrupted = true;
}
} finally {
//如果加锁过程出现异常,将节点从队列中取消
if (failed)
cancelAcquire(node);
}
}
/** waitStatus value to indicate thread has cancelled */
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking */
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition */
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3;
/**
* 获取当前节点的上一节点
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
}
/**
* 线程第一次进来查看前驱线程封装节点的ws初始为0会将前一节点的ws置为-1,退出方法,第二次
* 进入方法发现前驱节点状态为-1则返回true,表示当前线程可以parking了
* 每次线程parking前先将前驱的ws置为-1表示可unparking状态
* @pred当前线程封装节点的前一节点
* @node当前线程封装节点
*/
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
//如果当前线程节点的前一节点ws为-1则当前线程节点需要park
if (ws == Node.SIGNAL)
/*
* This node has already set status asking a release
* to signal it, so it can safely park.
*/
return true;
if (ws > 0) {
/*
* Predecessor was cancelled. Skip over predecessors and
* indicate retry.
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
//将当前节点的上一节点ws设置为-1
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
/**
*将当前线程park,并且返回当前线程是否被中断状态
*/
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
解锁
/**
* 释放锁资源
*/
public void unlock() {
sync.release(1);
}
/**
* 解锁
* @arg 解锁状态标识state-arg(数值为1)
*/
public final boolean release(int arg) {
//解锁更新锁状态标识,重入锁不将队列下一节点解阻塞,只有完全释放锁才能将下一节点解阻塞
if (tryRelease(arg)) {
Node h = head;
//如果不为持有锁节点,既为排队节点,ws!=0(被后一节点置为-1)
if (h != null && h.waitStatus != 0)
//解锁成功
unparkSuccessor(h);
return true;
}
//重入锁未全部解锁
return false;
}
/**
*进行解锁,非重入锁更新状态标识并设置锁持有线程为null,重入锁更新锁状态标识
*@releases 解锁状态标识state-releases(数值为1)
*/
protected final boolean tryRelease(int releases) {
//将锁状态标识-1
int c = getState() - releases;
//如果当前线程不为持有锁线程进行解锁报错
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
//当前线程锁是否完全解除
boolean free = false;
if (c == 0) {
//当前线程锁被完全解除
free = true;
设置当前持有锁线程为null
setExclusiveOwnerThread(null);
}
//更新锁状态标识
setState(c);
//返回锁是否完全释放标识
return free;
}
/**
*初始化锁当前线程的ws,将当前线程节点的下一节点unparking解阻塞
*@node 当前线程封装节点
*/
private void unparkSuccessor(Node node) {
//获取解锁线程的ws
int ws = node.waitStatus;
//将当前线程ws重新还原为0
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
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);
}