首先,比较下java两种线程锁:synchronized锁与Lock锁:
1.synchronized
优点:实现简单,语义清晰,便于JVM堆栈跟踪,加锁解锁过程由JVM自动控制,提供了多种优化方案,使用更广泛
缺点:悲观的排他锁,不能进行高级功能
2.lock
优点:可定时的、可轮询的与可中断的锁获取操作,提供了读写锁、公平锁和非公平锁,可实现更细粒度的锁,可以知道当前线程锁定状态
缺点:需手动释放锁unlock,不适合JVM进行堆栈跟踪
3.相同点
都是可重入锁
Lock锁有很多不同的类型,但是其实现都依赖AbstractQueuedSynchronizer类(简称AQS),这个类可以称的上是所有Lock锁的鼻祖。AQS是一个队列同步器,在AbstractQueuedSynchronizer维护一个队列,采用CAS操作更新该队列,当线程无法获取锁的时,会将该线程构造成一个node,然后添加至同步队列尾部。队列中的每一个阻塞线程通过队列中的其node的preNode唤醒,也就是,当队列中第一个排队的线程执行完毕之后,会唤醒第二个阻塞的线程,以此类推。
由于所有的Lock锁都以AQS为基础,实现思路都大同小异,本文以ReentrantLock作为讲解。
//ReentrantLock类实现Lock接口
public class ReentrantLock implements Lock, java.io.Serializable {
private final Sync sync;
//此类中Lea大神定义一个抽象的Sync继承AQS类,是因为ReenTrantLock提供公平锁和非公平锁。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
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;
}
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;
}
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
}
......
//公共的方法,直接面向使用者,实际上调用的是sync的lock方法
public void lock() {
sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
......
在ReentrantLock类中,定义了一个抽象的Sync类,该抽象类重写了AQS的tryRelease()方法,另外,有两个内部类继承Sync抽象类,NonfairSync和FairSync,分别是用来实现公平锁和非公平锁。在这两个Sync子类中重写了AQS的tryAcquire()方法。
//非公平锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
//公平锁
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
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;
}
}
我们以公平锁FairSync来分析:
1. 首先调用lock()方法,执行acquire(1),acquire()方法为AQS实现的方法
调用tryAcquire()方法(注意,该方法被ReenTrantLock类重写),如果返回true代表该线程可以立即执行,acquire()方法执行结束,调用lock()方法的线程可正常往下执行;如果返回false,代表未获取到同步资源,此线程阻塞,然后调用acquireQueued()方法,由于该方法里边有一个死循环,直到满足某个条件,所以从某种程度上说这个死循环相当于一个自旋锁,让该线程处于一种阻塞的状态;当acquireQueued()方法返回false时,表示当前线程之前的所有的并发线程已经执行完成,当前线程已经是可执行状态,若返回false,表示当前线程被中断或者取消,此时if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))条件成立,将会执行 selfInterrupt()方法将该线程中断。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
//该方法利用for循环不断的检测当前node的状态,直到该node的前驱结点执行,或者当前node被中断或取消
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);
}
}
//将当前node加入到同步队列的尾部
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;
}
总结
方法调用过程:
锁定:
lock() ——> acquire() ——> tryAcquire()(需要被我们自己重写)——> addWaiter() (tryAcquire()不满足,将当前线程加入到AQS同步队列中)——> acquireQueued()(从AQS队列中循环获取当前线程的阻塞状态)
解锁:
unLock() ——> release() ——>tryRelease()(需要被我们自己重写)——>AQS同步队列头结点出队列
通过以上分析,实际上Lock锁就是通过不断的循环AQS的同步队列,判断当前线程所在结点的前驱结点是否已经出队列,如果已经出队列,那么for循环终止,该死循环模拟了线程阻塞的过程。
**关键的部分:Sync类重写tryAcquire(),tryeRelease()方法,这两个方法分别对应
当前线程结点是否满足AQS队列入队和出队条件,当tryAcquire()返回false,表示当前线程需要被阻塞,即加入到AQS同步队列中,开始执行死循环(模拟阻塞状态);当在线程中执行unlock()方法时,在AQS中对应的操作即是从同步队列中出队列一个结点,并且唤醒下一个结点,该结点对应的线程结束死循环,变成可执行状态。**
参考:https://www.cnblogs.com/jiangds/p/6476293.html