Deep understanding of concurrent programming-ReentranLock

This article is for reading notes and books for the art of concurrent programming in Java

1. Fair lock

Fairness is for acquiring locks. If a lock is fair, the order of acquiring locks should conform to the absolute time order of the request, that is, FIFO.

When using fair lock, the lock method lock () call trace is as follows.
1) ReentrantLock: lock ().
2) FairSync: lock ().
3) AbstractQueuedSynchronizer: acquire (int arg).
4) ReentrantLock: tryAcquire (int acquires).
In step 4, the lock is actually started. Below is the source code of the method.

//java/util/concurrent/locks/AbstractQueuedSynchronizer.java
  public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
//java.util.concurrent.locks.ReentrantLock.FairSync
  protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            //  private volatile int state;  在 AbstractQueuedSynchronizer
            int c = getState();//获取锁的开始,首先读volatile变量state
            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;
        }

解锁:
1)ReentrantLock:unlock()。
2)AbstractQueuedSynchronizer:release(int arg)。
3)Sync:tryRelease(int releases)。

  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;
        }

Unfair lock

As long as the synchronization state is set successfully during spin, it means that the lock is acquired
.
1) ReentrantLock: lock ().
2) NonfairSync: lock ().
3) AbstractQueuedSynchronizer: compareAndSetState (int expect, int update).

   final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
            //获取不到锁
                acquire(1);
        }
//AbstractQueuedSynchronizer:compareAndSetState
  protected final boolean compareAndSetState(int expect, int update) {
        // See below for intrinsics setup to support this
        return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
    }

Unlock the same fair lock

3. The status of the lock cannot be obtained

  static ReentrantLock reentrantLock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {

        new Thread(() -> {
            m1();
        }, "t1").start();

        Thread.sleep(10);
		//观察t2
        new Thread(() -> {
            m2();
        }, "t2").start();

    }

    public static void m1() {
        reentrantLock.lock();
        try {
            try {
                //睡眠
                Thread.sleep(1000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        } finally {
            reentrantLock.unlock();
        }
    }

    public static void m2() {
        System.out.println("尝试获取锁");
        reentrantLock.lock();
        try {
            System.out.println("获取到锁");
        } finally {
            reentrantLock.unlock();
        }
    }

Insert picture description here
We know that synchronized locks cannot be acquired, and the state of these two locks is different.
As for the reason, it depends on the source code

  public final void acquire(int arg) {
  // 再次尝试获取状态,如果还是获取不到就把 当前线程加入队列 
  //返回值是线程是否在自旋的过程中被挂起(park),如果被挂起了,就唤醒
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();//  Thread.currentThread().interrupt();
    }
 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;
                    //退出自旋,放回线程是否被挂起 (park)
                    return interrupted;
                }
                //以上的条件都没有满足,就把当前线程park
              	
              	// 检查和更新未能获取的节点的状态。
				//如果线程阻塞,返回true。这是主信号
				//控制所有获取循环。需要pred == node.prev。
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
        //结束的时候,如果还是获取不到状态,那就取消这个节点了
            if (failed)
            //取消正在进行的获取尝试。
                cancelAcquire(node);
        }
    }
  private final boolean parkAndCheckInterrupt() {
 		 // 挂起挂起
        LockSupport.park(this);
        //返回当前线程状态 true
        return Thread.interrupted();
    }

So the thread is in the waiting state, that is, the thread is blocked during the spinning process;
that is, if (shouldParkAfterFailedAcquire (p, node) && parkAndCheckInterrupt ()) this key code

4. Summary

❑ When a fair lock and an unfair lock are released, a volatile variable state must be written at the end.
❑ When acquiring a fair lock, it first reads the volatile variable.
❑ When acquiring an unfair lock, the volatile variable is first updated with CAS. This operation has the memory semantics of both volatile read and volatile write.

There are at least two ways to implement the memory semantics of lock release-acquisition:
1) Use the memory semantics of write-read using volatile variables.
2) Use the memory semantics of volatile read and volatile writes attached to CAS.

Published 37 original articles · won praise 6 · views 4635

Guess you like

Origin blog.csdn.net/littlewhitevg/article/details/105584195