JAVA reentrant lock Revisited

Previous article, I will introduce a simple reentrant lock JAVA as many concurrent threads base (2) . There is also simple to introduce several properties reentrant lock, where we went to explore under which is how to achieve.

We know that when in use, the lock must first have defined, and then we took the current lock for locking operation, then the processing business, and finally release the lock operation (here took unfair achieve inside the lock to explain).

Bytecode manipulation

public class com.montos.lock.ReentrantLockDemo implements java.lang.Runnable {
  public static java.util.concurrent.locks.ReentrantLock lock;

  public static int k;

  public com.montos.lock.ReentrantLockDemo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public void run();
    Code:
       0: iconst_0
       1: istore_1
       2: iload_1
       3: sipush        1000
       6: if_icmpge     29                  //int类型的值进行栈顶比较
       9: getstatic     #2                  // Field lock:Ljava/util/concurrent/locks/ReentrantLock;
      12: invokevirtual #3                  // Method java/util/concurrent/locks/ReentrantLock.lock:()V
      15: getstatic     #4                  // Field k:I
      18: iconst_1
      19: iadd                              
      20: putstatic     #4                  // Field k:I
      23: iinc          1, 1
      26: goto          2
      29: iconst_0
      30: istore_1
      31: iload_1
      32: sipush        1000
      35: if_icmpge     50
      38: getstatic     #2                  // Field lock:Ljava/util/concurrent/locks/ReentrantLock;
      41: invokevirtual #5                  // Method java/util/concurrent/locks/ReentrantLock.unlock:()V
      44: iinc          1, 1
      47: goto          31
      50: return

  public static void main(java.lang.String[]) throws java.lang.InterruptedException;
    Code:
       0: new           #6                  // class com/montos/lock/ReentrantLockDemo
       3: dup
       4: invokespecial #7                  // Method "<init>":()V
       7: astore_1
       8: new           #8                  // class java/lang/Thread
      11: dup
      12: aload_1
      13: invokespecial #9                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
      16: astore_2
      17: new           #8                  // class java/lang/Thread
      20: dup
      21: aload_1
      22: invokespecial #9                  // Method java/lang/Thread."<init>":(Ljava/lang/Runnable;)V
      25: astore_3
      26: aload_2
      27: invokevirtual #10                 // Method java/lang/Thread.start:()V
      30: aload_3
      31: invokevirtual #10                 // Method java/lang/Thread.start:()V
      34: aload_2
      35: invokevirtual #11                 // Method java/lang/Thread.join:()V
      38: aload_3
      39: invokevirtual #11                 // Method java/lang/Thread.join:()V
      42: getstatic     #12                 // Field java/lang/System.out:Ljava/io/PrintStream;
      45: getstatic     #4                  // Field k:I
      48: invokevirtual #13                 // Method java/io/PrintStream.println:(I)V
      51: return

  static {};
    Code:
       0: new           #14                 // class java/util/concurrent/locks/ReentrantLock
       3: dup
       4: invokespecial #15                 // Method java/util/concurrent/locks/ReentrantLock."<init>":()V
       7: putstatic     #2                  // Field lock:Ljava/util/concurrent/locks/ReentrantLock;
      10: iconst_0
      11: putstatic     #4                  // Field k:I
      14: return
}
复制代码

There is nothing more than a stack, the stack comparison element, the stack into a variable these operations, without prior synchronizedinside the associated monitor command limit, some simply stack operations.

Locking Operation

final void lock() {
            if (compareAndSetState(0, 1)) //将同步状态从0变成1 采用cas进行更新
                setExclusiveOwnerThread(Thread.currentThread());//设置当前拥有独占访问权的线程。
            else
                acquire(1);//没有获取到锁,则进行尝试操作
        }
复制代码

Choose to go to the following:

 public final void acquire(int arg) {
        //先进行再次尝试获取锁的操作,如果获取失败则将当前加入队列中,并设置中断标志。
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
复制代码

First, go try to acquire the lock operation (here or take unfair lock):

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;
        }
复制代码

Then go down:

private Node addWaiter(Node mode) {
        //独占模式进行封装当前线程
        Node node = new Node(Thread.currentThread(), mode);
        Node pred = tail;
        if (pred != null) {如果尾节点不为null,将当前的节点接入并返回
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
复制代码

Continue to go down:

private Node enq(final Node node) {
        for (;;) {//
            Node t = tail;
            if (t == null) { // 初始化尾节点
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                node.prev = t;
                if (compareAndSetTail(t, node)) {//尾节点与当前的节点互换
                    t.next = node;
                    return t;//返回当前节点
                }
            }
        }
    }
复制代码

Then go back and go down:

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; // 手动清除引用  帮助GC
                    failed = false;
                    return interrupted;
                }
                //检测获取锁失败的节点状态  以及暂时挂起并返回当前的中断标志
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);//取消正在进行的获取尝试。
        }
    }
复制代码

Really, we look directly fails, then we go down:

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 {
            //waitstatus必须为0或传播。表明我们需要信号,但不要挂起。调用者重试以确保在挂起前无法获取。
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
复制代码

And a method of looking down:

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);//挂起当前线程
        return Thread.interrupted();//返回中断标识
    }
复制代码

Above the acquired queue inside the nodes do not read .. cancel cancelAcquire(node), which is to cancel the ongoing acquisition attempt. At the same time without having nodes removed. When the above operation is set to finish the current thread interrupt flag. There is to say after the main process if unsuccessful lock for the current thread is how to perform the operation, we can see, most of which method after obtaining less than a lock, the next step will try to get under again, If the acquisition will not proceed, we can get to the direct use, there is also multi-threaded operating inside charm 每一个空隙中就可能会让当前线程进行获得锁的操作.

Release the lock operation

The step of releasing the lock on many simple:

public final boolean release(int arg) {
        if (tryRelease(arg)) {//尝试释放锁
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);//唤醒节点的后续节点
            return true;
        }
        return false;
    }
复制代码

We continue to read:

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;//只有当前重入次数为0,才能返回true
                setExclusiveOwnerThread(null);//当前独占线程设为NULL
            }
            setState(c);//重新设置同步状态
            return free;
        }
复制代码

Then go down:

private void unparkSuccessor(Node node) {
        //当前状态为负数,则尝试清除当前的线程状态
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        //清除取消或无效的节点,从尾部向后移动以找到实际节点
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);//释放当前线程
    }
复制代码

Order from above to below, we found that the main thread lock to take the stage for a number of operations, and then to remove the thread from the waiting queue according to the state of the thread. When released it seems simple and a lot, we just need to see the state of the current thread-1, and then see whether it is reentrant.

We can see through a simple reentrant lock the code, something the author of operation with no lock to get a lock, this whole procedure there's a lot to consider, every moment, threads are likely to ever-changing, we need to understand is that we each case requires a step that may occur. If you can take into account what happens, then you can skip some steps, we can directly obtain the final result (this can be reflected in the thread tries to obtain a lock stage). A small reentrant lock partner for what perception can leave a message below, we can learn from each other and progress together ~

Guess you like

Origin juejin.im/post/5d11be136fb9a07f0b03d054