RTL 是用于管理线程并发的解决方案。接下来我们从最基本的用法来分析,为什么这个类会做到线程的原子性。一般用法 ReentrantLock rtl=new ReentranLock()。实际上内部默认的是利用非公平锁来实现。
public void lock() { sync.lock(); }此方法就是NonfairSync里面的lock()。
final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThread(Thread.currentThread()); else acquire(1); }注意这个compareAndSetState(0, 1),其实就是直接获取锁。大家注意获取锁其实就是将同步状态的state值置为1,这个同步状态的值就是stateOffset。为啥这个变量能够起到这个作用呢,原因在于这是一个private static final long stateOffset;什么意思,java里面你更改不了,但是通过cas算法确实可以直接变更,这就是同步的基石。这个跟synchronized 这个JVM里面的关键字的同步机制不一样,这个synchronized 锁的获取和释放是由JVM 直接控制。 继续看代码。这里我不分析cas 的原理,compareAndSetState(0, 1),实际调用的unsafe.compareAndSwapInt(this, stateOffset, expect, update)。这个方法很有意思,首先判断state的值是否为0,意思代表这个锁并没有被任何线程占用,那么我们可以占用,这个时候设置为1,然后接下来 setExclusiveOwnerThread(Thread.currentThread());设置当前线程占用了这个锁。如果有另外一个线程也要跑这个代码的时候,这个时候stateOffset 的值就是1了,这个时候就走到了 acquire(1);这块属于核心逻辑了。
public final void acquire(int arg) { if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) selfInterrupt(); }首先是tryAcquire(arg),是钩子方法,可以看到AQS 是采用模板设计模式。
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); //如果没有线程占据锁,那么直接获取,返回true. if (c == 0) { if (compareAndSetState(0, acquires)) { setExclusiveOwnerThread(current); return true; } } //这个时候获取到占据锁的线程,判断独占线程是不是自身。如果是的话。 state 加1.这个代表了非公平锁属于可重入。如果还是没有获取那么返回了false。 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; }acquireQueued(addWaiter(Node.EXCLUSIVE), arg) 这个方法首先来看。addWaiter(Node.EXCLUSIVE)。 实际上是将自身线程加入到等候队列。
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; //如果末尾Node有值,说明有线程在排队了。 if (pred != null) { //将末尾Node的引用赋值给Node。 node.prev = pred; //设置末尾Node为现有Node,意思就是加入到末尾。 if (compareAndSetTail(pred, node)) { //如果加入成功,那么将现有node引用赋值给之前末尾指针的next。这样现有node就排在了原先末尾的后面。 pred.next = node; return node; } } //如果有tail和没有tail都会走这个路基。 enq(node); return node; }分析enq(node)。实际上主要的也就是将Node设置为head或者设置为tail。如果没有tail那么必然没有head,这个时候直接设置head。如果有tail,那么将node加载后面。注意这里是个死循环,代表但是肯定会跳回来的。
private Node enq(final Node node) { 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,代表将独占模式的的Node入队尾了。接下来分析acquireQueued(Node)方法。都写着final呢,这个是模板方法了。
final boolean acquireQueued(final Node node, int arg) { boolean failed = true; try { boolean interrupted = false; for (;;) { //获取到node前面的那个Node,实际就是没有加Node.EXCLUSIVE之前的末尾结点。 final Node p = node.predecessor(); //如果p就是head,那么代表只有一个节点。 if (p == head && tryAcquire(arg)) { setHead(node); p.next = null; // help GC failed = false; return interrupted; } //判断是否需要阻塞现场。如果阻塞线程了,那么是需要等待唤醒的。否则会一直走死循环,直到获取到锁。阻塞的前提是前面的线程信号是SIGNAL. if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) interrupted = true; } } finally { if (failed) cancelAcquire(node); } } private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) { int ws = pred.waitStatus; //如果当前线程的前置节点为SIGNAL,代表可以阻塞了,线程阻塞会 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 { /* * waitStatus must be 0 or PROPAGATE. Indicate that we * need a signal, but don't park yet. Caller will need to * retry to make sure it cannot acquire before parking. */ //设置前置线程SIGNAL 标志。 compareAndSetWaitStatus(pred, ws, Node.SIGNAL); } return false; }以上就是获取锁的流程。现在分析下解锁的流程。
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是钩子方法。只有当state==0 才能进行真正释放。
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; }这就是非公平锁的锁机制,为啥说非公平呢,以为不管任何线程想要获取锁,都是先去请求锁,取不到才进入等待队列。公平锁的机制和这差不多。至于说Condition机制后续再找时间分析吧。