并发包下面的源码分析帖子早就烂大街了,但别人总结的永远不是自己理解的,不会的时候再去看别人总结好的东西,下面是整理的学习ReentrantLock的lock。
首先看下ReentrantLock的构造器
public ReentrantLock() {
sync = new NonfairSync();
}public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
ReentrantLock其实是分为公平锁和非公平锁,默认的是非公平锁,NonfairSync和FairSync都继承了 AbstractQueuedSynchronizer(下面简称AQS)队列同步器,具体这两种锁的表现最后会总结。
先看下非公平锁的源码
final void lock() { 在AQS中有个重要的states,如果这个states是0的话证明锁是释放状态,这块乐观锁尝试states加到1 如果成功那么将当前线程设置为锁的拥有者 if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else 如果没成功那么将这个线程放到队列 acquire(1); }乐观锁尝试states加到1
protected final boolean compareAndSetState(int expect, int update) { return unsafe.compareAndSwapInt(this, stateOffset, expect, update); }
现在重点看下AQS中的acquire方法
public final void acquire(int arg) { if (!tryAcquire(arg) && 如果当前线程没有获取锁,addWaiter设置Node为独占模式,轮询唤醒队列中的当前线程,返回中断状态 acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } tryAcquire方法的实现在具体的子类中,这个方法还是用当前线程去尝试获取锁 final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { 如果现在states是0的话那么再次做尝试获取锁的尝试(同lock方法) if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } 如果当前线程就是锁的拥有者state增加(说白了就是支持重入,毕竟重入锁) 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; } 在AQS中分为两种模式,一种是独占EXCLUSIVE,一种是共享SHARED private Node addWaiter(Node mode) { Node node = new Node(Thread.currentThread(), mode); Node pred = tail; if (pred != null) { node.prev = pred; if (compareAndSetTail(pred, node)) { pred.next = node; return node; } } enq(node); return node; }再重点看下AQS中的acquireQueued方法
此方法干的事就是轮询,等前面的资源唤醒自己(返回的事自己是否被中断过,false未被中断过)
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { 是否被中断过 boolean interrupted = false; 轮询 for (;;) { 当前节点的上一个节点 final Node p = node.predecessor(); 如果上一个节点是头并且tryAcquire,熟悉不?上面有啊,当前线程尝试获取锁 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); } } 检查当前节点是否进入了wait状态 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; }private final boolean parkAndCheckInterrupt() {
使线程进入waiting状态
LockSupport.park(this);
你调用interrupted方法,第一次会返回true。
然后,当前线程的中断状态被方法内部清除了。
第二次调用时就会返回false
interrupted是静态方法,返回的是当前线程的中断状态
第一次使用该方法如果已经中断,
第二次使用该方法则会取消中断。
调用interrupted方法,第一次会返回true。
然后,当前线程的中断状态被方法内部清除了。
第二次调用时就会返回false。
return Thread.interrupted();
}
看公平锁对比非公平锁来看
final void lock() { acquire(1); } 可以看到公平锁不会有去让当前线程去再次尝试拿到锁的动作,直接acquire,进去acquire方法中和非公平锁唯一的不同就是tryAcquire中的实现。这个方法也很简单 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; }
总结:非公平锁是当线程来了先拿当前线程和队列中的头进行竞争拿锁,而公平锁直接是将线程加入到队尾等待前面的节点唤醒, 从效率上来讲非公平锁更高效,减少了唤醒等待和线程切换,不过这也可能有造成队列中的线程等待时间较长的情况。