AQS 独占式锁如何实现线程同步

独占式锁为了实现线程同步,主要是综合使用了三种手段。

  1. 双向链表构成的队列
  2. CAS原子操作
  3. 阻塞与释放(ParkSupport的park( )和unpark( )方法)

线程获得锁的流程如下:

  1. 先请求获得锁, 如果成功了就没什么说得,如果不成功则请求队列
  2. 请求队列的过程如图:
    在这里插入图片描述

重要代码跟踪:

// lock方法调用 acquire 方法请求获得锁
 final void lock() {
            acquire(1);
 }
        
// 这是获得锁的方法,参数arg的代表锁的类型:独占锁为1
 public final void acquire(int arg) {
		// 可以看出,如果不能获得锁,则请求队列。再请求队列时先生成节点
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
-----------------------------------------------------------------------------------------------
@节点的添加
// 这是生成节点的具体实现
private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        Node pred = tail;
       // 如果尾部节点为null,说明当前队列还没有节点
        if (pred != null) {
        	 // 设置新节点的前置节点为队列的尾部节点
            node.prev = pred;
            // 并做添加操作,如果成功则返回当前节点
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        // 这个方法是使用CAS的自旋确保节点成功添加,原代码见下一个方法
        enq(node);
        return node;
    }
// 通过CAS加自旋添加节点
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;
                }
            }
        }
    }
-----------------------------------------------------------------------------------------------
// 请求队列
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;
                    return interrupted;
                }
                 // 如果不能获得锁,就进入阻塞状态
                if (shouldParkAfterFailedAcquire(p, node) &&
                	// 代码见下一个方法
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
// 阻塞的方法
private final boolean parkAndCheckInterrupt() {
		//   阻塞
        LockSupport.park(this);
        return Thread.interrupted();
    }
-----------------------------------------------------------------------------------------------
-----------------------------------------------------------------------------------------------
阻塞的方法什么时候被唤醒?
// 释放锁
public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
            	// 尝试释放锁成功后,唤醒下一个被阻塞节点,代码见下一个方法
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
// 唤醒当前节点(参数node)的下一个节点
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)
        	// 如果下一个节点不为null,就唤醒。被唤醒的节点处在for循环中,继续做自己的事。新一轮请求获得锁逻辑开始。
            LockSupport.unpark(s.thread);
    }

猜你喜欢

转载自blog.csdn.net/weixin_37077950/article/details/83508378