预备知识
锁一般用于控制多线程并发访问同一资源,只有获得锁的线程才可以访问,保证每次只会被一个线程访问。
两种锁:
非阻塞锁 | spin lock(自旋锁) | 等待锁的线程,会一直重试获得锁,不会阻塞,也是自旋锁名字的由来 | 用于锁时间非常短的情况,线程状态变为阻塞态的成本高于自旋 |
阻塞锁 | mutex lock(互斥锁) | 等待锁的线程会依次进入队列,线程还有可能变为阻塞态,等待被唤醒 | 用于锁时间较长的情况 |
锁的实现一般需要用到cpu的原子性指令来实现,如:lock指令前缀。
原子性指令:汇编指令不是cpu执行的最小单元,一条汇编指令(如:add ax,1)cpu会分为若干小的步骤去执行,cpu也不是一条指令接一条指令去执行的,会同时处理多条指令,所以汇编指令执行过程不是原子性的是可分的。原子性指令就是要保证指令执行时不可分,并发执行同一原子指令会变成顺序执行,是cpu级别的并发控制,cpu性能会有一些损耗。
ReentrantLock是利用了cas实现的,cas可以原子性修改变量值的。
正文
一、类继承关系
Lock 锁接口
-ReentrantLock 可重入锁
AbstractOwnableSynchronizer 拥有者同步器
-AbstractQueuedSynchronizer 排队同步器
-ReentrantLock.Sync 同步器
-ReentrantLock.NonfairSync 非公平同步器
二、数据结构
ReentrantLock
-
private final Sync sync;
AbstractOwnableSynchronizer
拥有者同步器
-
private transient Thread exclusiveOwnerThread; //获得锁的线程, 获得线程把自己保存在其中
AbstractQueuedSynchronizer 排队同步器, 这是核心类,实现了
-
private transient volatile Node head; //队列头
-
private transient volatile Node tail; //队列尾
-
private volatile int state; //锁的状态, 默认是0, 锁空闲, 获得锁时改为1, 重入锁一次加1
AbstractQueuedSynchronizer.
Node 队列节点,
AbstractQueuedSynchronizer的内部类
-
volatile int waitStatus; //等待状态, 0:默认状态,-1:后驱节点需要被unpark
-
volatile Node prev; //后驱节点
-
volatile Node next; //前驱节点
-
volatile Thread thread;
-
Node nextWaiter;
ReentrantLock是一层包装,实现都是在同步器中。
三、获取锁调用过程
获取锁的流程图
lock方法:先用cas指令修改锁状态,如果不成功,调用acquire获取锁方法
//ReentrantLock构造方法
public ReentrantLock() {
sync = new NonfairSync(); //默认使用非公平锁
}
//ReentrantLock的lock方法, sync默认是非公平锁
public void lock() {
sync.lock();
}
//NonfairSync的lock方法
final void lock() {
if (compareAndSetState(0, 1)) //cas指令修改锁状态status,从0改为1
setExclusiveOwnerThread(Thread.currentThread()); //status修改成功, 设置锁拥有者线程为当前线程
else
acquire(1); //获取锁, 传入的参数1, 用于修改锁状态status, 第一次获得锁status改为1
}
acquire方法:先尝试获取锁,如果不成功,入队列在队列中获取锁
//AbstractQueuedSynchronizer的acquire方法
public final void acquire(int arg) {
if (!tryAcquire(arg) && //尝试获得锁, 若成功就return了, 若失败加入队列等待
acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) //加入队列等待, 然后在队列中获取锁
selfInterrupt();
}
tryAcquire方法:尝试获取锁
//NonfairSync的tryAcquire方法, 尝试获取锁
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
//Sync的nonfairTryAcquire方法
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) { //同步状态status为0, 表示该线程第一次获得锁
if (compareAndSetState(0, acquires)) { //cas指令修改一下同步状态status
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) { //当前线程重入获取锁, state加1
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
addWaiter方法:入队列
//AbstractQueuedSynchronizer的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) { //判断有没有初始化, tail指向null, 队列没有初始化
node.prev = pred;
if (compareAndSetTail(pred, node)) { //先用cas指令加入一下队列
pred.next = node;
return node;
}
}
enq(node); //调用入队列方法
return node;
}
//AbstractQueuedSynchronizer的enq方法, 加入队列方法
private Node enq(final Node node) {
for (;;) { //一直用cas指令去加入队列, 直到成功
Node t = tail;
if (t == null) { // Must initialize, 初始化队列, 把head设置一个Node, tail指向head
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
acquireQueued方法:在队列中获取锁
//AbstractQueuedSynchronizer的acquireQueued方法, 在队列中获取锁
//入队列后, 如果前驱节点是head, 尝试获取锁, 获取失败就park
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, 尝试获取锁
setHead(node); //获得锁后, 把当前节点设置成head, 并把节点中的thread设为null
p.next = null; // help GC
failed = false;
return interrupted;
}
//前驱节点不是head, 前驱节点的waitStatus设为Node.SIGNAL
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) //前驱节点设置成功, 当前线程park, 等待前驱节点释放锁并被unpark
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
总的来说使用了cas指令、队列、park(unpark)来实现了锁的功能。
还可以深入的知识
- AbstractQueuedSynchronizer 中的队列,使用了cas指令来控制并发,是一种自旋的方式;
- park和unpark这对方法,是不分前后的,一个线程可以先park再被unpark,也可以先unpark再park,这时会立即返回不会阻塞;