private ReentrantLock reentrantLock = new ReentrantLock(); public void test (){ reentrantLock.lock(); System.out.println(Thread.currentThread().getName()+ "test....begin"); try { Thread.sleep(10000L); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"test....end"); reentrantLock.unlock(); } 复制代码
ReentrantLock 提供两种方式加锁 公平和非公平 默认为非公平 如果要使用非公平构造函数传true 加公平锁调用FairSync.lock() 加非公平锁调用NonfairSync.lock() 释放锁调用AbstractQueuedSynchronizer.release(int arg)
NonfairSync.lock 代码
final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); } 复制代码
AbstractQueuedSynchronizer.acquire(int arg)代码
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); } 复制代码
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); 获取state字段的值 if (c == 0) { 如果值是0 代表没有线程占用 if (compareAndSetState(0, acquires)) { 通过CAS替换看自己是否可以占用成功 setExclusiveOwnerThread(current); return true; 如果成功就返回true } } 如果有线程占用,判断占用的线程和自己是否是同一个线程 else if (current == getExclusiveOwnerThread()) { //如果是同一个线程 state字段加上传入的参数 int nextc = c + acquires; if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); //更新state setState(nextc); //从这可以看出 NonfairSync.lock是重入锁 return true; } //如果都没满足,代表加锁不成功 return false; } 复制代码
private Node addWaiter(Node mode) { //把自己当前线程封装成一个node 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; //就把自己node的上一个结点设置为队尾那个结点 更新自己结点为队尾结点 if (compareAndSetTail(pred, node)) { //如果更新成功,把上一个结点的下一个结点设置为自己结点 pred.next = node; //返回当前结点 return node; } } enq(node); return node; } 复制代码
如果队尾为空或者更新自己为队尾结点失败继续enq(node) AbstractQueuedSynchronizer.enq片段
private Node enq(final Node node) { 无限循环CAS操作,直到把自己变成队尾结点 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; } } } } 复制代码
总结:addWaiter(Node.EXCLUSIVE) 是组装当前线程结点,然后通过CAS添加到队尾
AbstractQueuedSynchronizer.acquireQueued代码片段
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { //获得当前结点的前一个结点 final Node p = node.predecessor(); //如果head是前一个结点,我就再次尝试获取锁,万一获取成功了呢 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(p, node) &&parkAndCheckInterrupt() 先看shouldParkAfterFailedAcquire(p, node) AbstractQueuedSynchronizer.shouldParkAfterFailedAcquire代码片段
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { //入参是前一个结点和当前结点 int ws = pred.waitStatus; if (ws == Node.SIGNAL) //如果前一个是-1,代表可以安心的等待 return true; if (ws > 0) { //如果前一个大于0,代表前一个等待超时或者被中断了,需要从同步队列中取消该Node的结点,所以继续找在等待的前结点,把找到的结点的下一个结点设为当前结点 do { node.prev = pred = pred.prev; } while (pred.waitStatus > 0); pred.next = node; } else { //如果是小于0的其他状态都统一设为-1,等待状态 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; } 复制代码
这个方法其实就是如果前驱结点的状态不是SIGNAL,那么自己就不能安心去休息,需要去找个安心的休息点,然后自己设为这个前驱结点的后一个结点
附上node结点的waitStatus含义:
如果找到安全休息点后就继续parkAndCheckInterrupt方法 真正让线程等待的方法 AbstractQueuedSynchronizer.parkAndCheckInterrupt代码片段
private final boolean parkAndCheckInterrupt() { LockSupport.park(this);//调用park()使线程进入waiting状态 return Thread.interrupted();//如果被唤醒,查看自己是不是被中断的。 } 复制代码
park()会让当前线程进入waiting状态。在此状态下,有两种途径可以唤醒该线程:1)被unpark();2)被interrupt()。需要注意的是,Thread.interrupted()会清除当前线程的中断标记位
前面已经介绍了NonfairSync.lock(),再看下FairSync.lock() FairSync.lock()代码片段
final void lock() { acquire(1); } 复制代码
相比于NonfairSync.lock(),少了一个CAS判断,因为是公平锁,所以不像非公平锁那样直接上来你尝试自己能不能获得锁 后面调用逻辑只是在FairSync.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; } } 复制代码
比较Sync.nonfairTryAcquire(int acquires)只是多了!hasQueuedPredecessors()这个判断 就是在尝试获取锁的时候,不是直接去获取,而是看队列中有没有其他等待的线程,如果有,自己是不能直接去获取锁的
小结:ReentrantLock的公平锁和非公平锁加锁就讲完了,公平和非公平体现在 公平获取锁时是有先来后到的,非公平在尝试获取锁时,是不管队列中是否有其他线程在等待,自己直接去CAS尝试加锁,如果不成功才放入队列
ReentrantLock.unlock()其实调用的是AbstractQueuedSynchronizer.release(1) AbstractQueuedSynchronizer.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调用的是ReentrantLock.tryRelease 代码片段
protected final boolean tryRelease(int releases) { int c = getState() - releases;//计算state状态量 if (Thread.currentThread() != getExclusiveOwnerThread()) throw new IllegalMonitorStateException(); boolean free = false; if (c == 0) {//如果是0代表可以释放 free = true; setExclusiveOwnerThread(null);//清空锁资源占有的线程 } setState(c);设置变量 return free; } 复制代码
这个是重入锁释放的设计,因为在获取锁时,同一个线程可以多次对同一个资源加锁的,每加一次的时候state都会加1,释放也一样都会减一,只有当state等于0的时候才代表这个线程释放完了,所以在写ReentrantLock时,lock和unlock要成对出现,否则会一直在那占有资源
接下来看AbstractQueuedSynchronizer.unparkSuccessor代码片段
private void unparkSuccessor(Node node) { //这里,node一般为当前线程所在的结点 int ws = node.waitStatus; if (ws < 0) //置零当前线程所在的结点状态,允许失败。 compareAndSetWaitStatus(node, ws, 0); Node s = node.next; //找到下一个需要唤醒的结点s if (s == null || s.waitStatus > 0) {//如果为空或已取消 s = null; for (Node t = tail; t != null && t != node; t = t.prev) if (t.waitStatus <= 0) //从这里可以看出,<=0的结点,都是还有效的结点 s = t; } if (s != null) LockSupport.unpark(s.thread); //唤醒 } 复制代码
最终就是找到下一个有效的结点,然后唤醒该结点对应的线程