AQS ReentrantLock ReentrantReadWriteLock CountDownLatch源码阅读

8 篇文章 0 订阅
1 篇文章 0 订阅

1. AQS源码阅读

在这里插入图片描述

1.1 AQS简介

AbstractQueuedSynchronizer 简称AQS,是实现JUC包中各种锁的关键,此类是一个模板类,具体的ReentrantLockCountDownLatchReadWriteLock等等都是自己去实现里边变量的使用规则。

各种类型的锁都有自己的锁类型信息

  • 比如ReadWriteLock就肯定会有当前的锁状态是读锁模式还是写锁模式

    static final Node SHARED = new Node(); // 当前锁状态是 共享锁(读锁)模式
    static final Node EXCLUSIVE = null;    // 当前锁状态是 排它锁(写锁)模式
    

这些所有的信息加上线程本身被封装成一个内部类对象 Node

每个Node都有指向前驱节点和后继节点的指针,每个节点将有关线程的某些控制信息保存在其节点的前身中。

1.2 AQS内部类详解

1.2.1 Node

  • 用作等待队列
static final class Node {
    static final Node SHARED = new Node(); // 当前锁状态是 共享锁(读锁)模式
    static final Node EXCLUSIVE = null;    // 当前锁状态是 排它锁(写锁)模式
	
    static final int CANCELLED =  1;		// 表示线程被取消(执行过interrupt()方法或者 timeout )
    
    static final int SIGNAL    = -1;		// 等待队列中 此线程后边的节点需要被 notify()、或者 LockSupport.unpark()【公平锁?】
    
    static final int CONDITION = -2;		// 此线程在一个 condition 等待队列中
    
    static final int PROPAGATE = -3;   		// 暂时不知道是啥用(即使是头结点是共享锁模式好像也到排它锁的时候停止了)
	
    /* 存储一个上边的某一个值,表示当前节点的状态 */
    volatile int waitStatus;

    // 当前节点的前一个节点,用来监视前一个节点的状态(如果前一个节点被取消,自己连接到前一个节点的前一个节点)
    volatile Node prev;

    // 当前节点的后一个节点,当这个节点当做头结点释放锁的时候,通知下一个节点
    volatile Node next;

    // 节点中保存的 线程
    volatile Thread thread;

    // 指向一个 condition等待队列
    Node nextWaiter;
}

1.2.2 ConditionObject

  • new ReentrantLock().newCondition()创建的对象
public class ConditionObject implements Condition, java.io.Serializable {
        // condition等待队列的第一个节点
        private transient Node firstWaiter;
       	// condition等待队列的最后一个节点
        private transient Node lastWaiter;
}

1.3 非公平模式图解

在这里插入图片描述

1.4 公平模式图解

在这里插入图片描述

1.5 线程被打断

在这里插入图片描述

1.6 进入Condition等待队列

1.6.1

在这里插入图片描述

1.6.2

在这里插入图片描述

1.6.3 终于用上了nextWaiter指针

在这里插入图片描述

1.7 AQS类变量详解

// 表示锁当前的状态,初始为0, ReentrantLock.lock()之后数字加1,表示已经上锁
private volatile int state;
// 等待队列的头结点(head节点初始化的时候,waitStatus会被赋值为0)
private transient volatile Node head;
// 等待队列的尾巴节点
private transient volatile Node tail;

// 通过 VarHander 直接获取上边三个变量的地址,直接使用变量地址来进行CAS操作
private static final VarHandle STATE;
private static final VarHandle HEAD;
private static final VarHandle TAIL;
static {
    try {
         /* handle 绑定到一个了个类的属性上,通过位于类的类型,变量的名字 和 变量本身的类型 */
        MethodHandles.Lookup l = MethodHandles.lookup();
        STATE = l.findVarHandle(AbstractQueuedSynchronizer.class, "state", int.class);
        HEAD = l.findVarHandle(AbstractQueuedSynchronizer.class, "head", Node.class);
        TAIL = l.findVarHandle(AbstractQueuedSynchronizer.class, "tail", Node.class);
    } catch (ReflectiveOperationException e) {
        throw new ExceptionInInitializerError(e);
    }
}

1.8 插入到AQS队列

插入到AQS队列中只需要对当前的tail节点执行一次CAS操作

prev主要用于处理线程取消(线程超时或者被打断)的情况。如果取消某个节点,则其后继节点(通常)会重新链接到未取消的前任节点。

加入队列就是原子操作更新tail节点的next对象

2 ReentrantLockAQS

在这里插入图片描述

2.1 lock()

获得锁

CAS更改state的值,如果当前为0,线程1给他加到了1,表示线程1抢到了这把锁,其余线程进入等待队列,CAS更新tail节点的next指针

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    // 获取state的值
    int c = getState();
    if (c == 0) {
        // 如果为0,CAS替换为1
        if (compareAndSetState(0, acquires)) {
            // 替换成功设置自己为独占线程
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    // 没有CAS成功则表示已有线程获得锁,
    // 然后看看线程是不是自己(可重入)
    else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        // 把 state 的值 ++ 
        setState(nextc);
        return true;
    }
    return false;
}

// 如果获取锁失败(没抢到 + 自己也不是独占线程)
// 就要把自己加到等待队列中,等待队列的实现使用一个AQS的内部类实现,Node,
// 把锁模式设置为独占模式
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))


private Node addWaiter(Node mode) {
    Node node = new Node(mode);

    for (;;) {
        // CAS死循环往队尾加
        Node oldTail = tail;
        if (oldTail != null) {
            // 用VarHandle给普通变量做CAS操作,把新申请的节点的pre设置为尾巴节点
            node.setPrevRelaxed(oldTail);
            // 再用 CAS 给尾巴的 next 设置 新node
            if (compareAndSetTail(oldTail, node)) {
                oldTail.next = node;
                return node;
            }
        } else {
            // 当前节点时第一个进队等待的节点,初始化等待队列
            initializeSyncQueue();
        }
    }
}


final boolean acquireQueued(final Node node, int arg) {
    boolean interrupted = false;
    try {
        for (;;) {
            // 死循环,查看自己的前一个节点是否变成了头结点
            final Node p = node.predecessor();
            // 如果不是头结点直接短路,
            // 是头结点就开始尝试获得锁
            if (p == head && tryAcquire(arg)) {
                // 获取锁成功,就把自己设置为头结点
                setHead(node);
                // 上一个已经执行完成,上一个出队的时候把他的next置位空
                p.next = null; // help GC
                return interrupted;
            }
            // 这个线程尝试获取锁失败了,应该停下吗,是不是应该不在浪费CPU的空转呢?
            // 第一次调用此方法之后前驱节点的值肯定被替换为了 -1 , 第二次调用的时候以true返回,进入if
            if (shouldParkAfterFailedAcquire(p, node))
                interrupted |= parkAndCheckInterrupt(); // 调用lockSupport.park()阻塞线程
        }
    } catch (Throwable t) {
        cancelAcquire(node);
        if (interrupted)
            selfInterrupt();
        throw t;
    }
}

// 这个线程尝试获取锁失败了,应该停下吗,是不是应该不在浪费CPU的空转呢?
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
    int ws = pred.waitStatus;
    // 前一个节点的状态是 -1   // 等待队列中 此线程后边的节点需要被 notify()
    if (ws == Node.SIGNAL)
        return true;
    // 前驱节点已经被取消(打断或者超时)
    if (ws > 0) {
        do {
            // 我就得找到没有被取消的前一个,看看他的状态
            node.prev = pred = pred.prev;
        } while (pred.waitStatus > 0);
        // 并把找到的没取消的next指针指向自己
        pred.next = node;
    } else {
        // 前一个线程的状态肯定是 0 或者 PROPAGATE = -3;, 用CAS替换为 -1
        pred.compareAndSetWaitStatus(ws, Node.SIGNAL);
    }
    return false;
}

可重入

可重入实现为给state属性值往上加1

公平性

只有state属性再次为0的时候表示线程1彻底释放了锁资源,就可以通过上述过程来选择

  • 非公平:新来的线程还在自旋状态的时候可以争抢锁资源,队列中的节点只激活头结点的下一个

  • 公平:新来的线程也不可以抢锁

2.2 trylock()

尝试获取锁,获取不到锁或者过了超时时间就把自己设置为取消状态,并且更新前后节点的指向关系

curNode.next.pre = curNode.pre

curNode.pre.next = curNode.next

private boolean doAcquireNanos(int arg, long nanosTimeout)
            throws InterruptedException {
    if (nanosTimeout <= 0L)
        return false;
    // 设置线程超时时间
    final long deadline = System.nanoTime() + nanosTimeout;
    final Node node = addWaiter(Node.EXCLUSIVE);
    try {
        for (;;) {
            // 这里跟普通获取锁的逻辑一样
            final Node p = node.predecessor();
            if (p == head && tryAcquire(arg)) {
                setHead(node);
                p.next = null; // help GC
                return true;
            }
            // 查看自己还能活多久
            nanosTimeout = deadline - System.nanoTime();
            // 已经超时,取消获取锁
            if (nanosTimeout <= 0L) {
                // 已经超时,取消获取锁,把自己取消,更改自己在等待队列中的前后关系
                cancelAcquire(node);
                return false;
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                // 还有没有必要把线程阻塞?有可能阻塞过程执行的时间都要比他过期时间短,那我阻塞他还有什么用呢?
                // 不如留着他自己过期的了
                nanosTimeout > SPIN_FOR_TIMEOUT_THRESHOLD)
                LockSupport.parkNanos(this, nanosTimeout);
            if (Thread.interrupted())
                throw new InterruptedException();
        }
    } catch (Throwable t) {
        cancelAcquire(node);
        throw t;
    }
}

2.3 lockInterruptibly()

节点中线程可以被打断,打断之后的线程也设置为取消状态,并且更新节点的指向关系

private void doAcquireInterruptibly(int arg)
   throws InterruptedException {
   final Node node = addWaiter(Node.EXCLUSIVE);
   try {
       for (;;) {
           final Node p = node.predecessor();
           if (p == head && tryAcquire(arg)) {
               setHead(node);
               p.next = null; // help GC
               return;
           }
           if (shouldParkAfterFailedAcquire(p, node) &&
               parkAndCheckInterrupt())
               // 如果在其他线程中调用了该线程的  interrupt 方法,parkAndCheckInterrupt 就会返回true
               //从而跳入 if  抛出异常
               throw new InterruptedException();
       }
   } catch (Throwable t) {
       cancelAcquire(node);
       throw t;
   }
}

private final boolean parkAndCheckInterrupt() {
   LockSupport.park(this);
   // 如果在其他线程中调用了该线程的  interrupt 方法,就会返回true
   return Thread.interrupted();
}
   

2.4 condition.await()


public final void await() throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    // 新建一个 waitStatus = -2 (CONDITION)的节点
    Node node = addConditionWaiter();
    // 释放锁,并把现在已经加到队列中的线程激活
    int savedState = fullyRelease(node);
    int interruptMode = 0;
    // node 是 CONDITION状态 进入while, 阻塞线程
    while (!isOnSyncQueue(node)) {
        LockSupport.park(this);
        if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
            break;
    }
    // acquireQueued 把自己阻塞
    if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
        interruptMode = REINTERRUPT;
    if (node.nextWaiter != null) // clean up if cancelled
        unlinkCancelledWaiters();
    if (interruptMode != 0)
        reportInterruptAfterWait(interruptMode);
}


private Node addConditionWaiter() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node t = lastWaiter;
    // If lastWaiter is cancelled, clean out.
    // lastWaiter 上一次被加到condition等待队列的节点
    // t 为 空,则当前condition中没有节点等待,或者上一个等待节点的 waitStatus 已经被改变 (被取消)
    if (t != null && t.waitStatus != Node.CONDITION) {
        // 删除上一个节点,并继续把t设置为还在condition等待队列中的节点
        unlinkCancelledWaiters();
        t = lastWaiter;
    }
	// 新建
    Node node = new Node(Node.CONDITION);
	// 等待队列为空的话,自己就是第一个
    if (t == null)
        firstWaiter = node;
    else
        t.nextWaiter = node; // 不为空的话就把自己连接在上一个后边
    lastWaiter = node; // 自己是刚刚加进来最后等待的节点
    return node;
}


// 调用await() 的时候释放锁,激活正在等待对列的线程
final int fullyRelease(Node node) {
    try {
        int savedState = getState();
        // 调用await() 的时候释放锁,激活正在等待对列的线程
        if (release(savedState))
            return savedState;
        throw new IllegalMonitorStateException();
    } catch (Throwable t) {
        node.waitStatus = Node.CANCELLED;
        throw t;
    }
}


// 调用await() 的时候释放锁,激活正在等待对列的线程
 public final boolean release(int arg) {
     if (tryRelease(arg)) {
         Node h = head;
         if (h != null && h.waitStatus != 0)
             // 调用await() 的时候释放锁,激活正在等待对列的线程
             unparkSuccessor(h);
         return true;
     }
     return false;
 }


private void unparkSuccessor(Node node) {
    // ... 过滤代码(有可能头上的节点已经被取消,从队列头开始找到第一个没有取消的节点)
    // unpark一个线程,这个线程不是某个等待队列中找的,而是依照在创建线程节点时的先后顺序
    // 挑一个可以激活的进行激活操作,让后者获得此时的锁,
    // 如果后者也会因为当前的系统状态阻塞,则继续park自己向后激活(使用的next指针向后找)
    // condition 中使用的指针是 nextWaiter
    if (s != null)
        LockSupport.unpark(s.thread);
}

2.4 condition.signal()

public final void signal() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    // 等待队列不为空,叫醒第一个线程
    if (first != null)
        doSignal(first);
}


private void doSignal(Node first) {
    do {
        if ( (firstWaiter = first.nextWaiter) == null)
            lastWaiter = null;
        first.nextWaiter = null;
        // 叫醒等待队列中的第一个线程,因为有可能头上的线程已经被取消,所以需要找到队列中第一个没有被取消的线程
    } while (!transferForSignal(first) &&
             (first = firstWaiter) != null);
}


// 叫醒第一个线程,因为有可能头上的线程已经被取消,所以需要找到队列中第一个没有被取消的线程
final boolean transferForSignal(Node node) {
    /*
         * If cannot change waitStatus, the node has been cancelled.
         */
    if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
        return false;

    /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
    Node p = enq(node);
    int ws = p.waitStatus;
    if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
        LockSupport.unpark(node.thread);
    return true;
}

2.4 condition.signalAll()

public final void signalAll() {
    if (!isHeldExclusively())
        throw new IllegalMonitorStateException();
    Node first = firstWaiter;
    // 第一个等待节点不为空,叫醒所有线程
    // 循环叫醒所有线程,调用 LockSupport.unpark
    if (first != null)
        doSignalAll(first);
}

private void doSignalAll(Node first) {
    // 因为此时要把所有线程叫醒了,所以把这两个变量置位空
    lastWaiter = firstWaiter = null;
    do {
        // 循环叫醒该等待队列中的所有线程,调用 LockSupport.unpark
        // 加到condition中调用的方法中 给 nextWaiter 赋值就是加到condition中的线程
        // 虽然他们都有next指针,但是next指针是说他们创建节点时的顺序
        Node next = first.nextWaiter;
        first.nextWaiter = null;
        transferForSignal(first);
        first = next;
    } while (first != null);
}

final boolean transferForSignal(Node node) {
    /*
         * If cannot change waitStatus, the node has been cancelled.
         */
    if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
        return false;

    /*
         * Splice onto queue and try to set waitStatus of predecessor to
         * indicate that thread is (probably) waiting. If cancelled or
         * attempt to set waitStatus fails, wake up to resync (in which
         * case the waitStatus can be transiently and harmlessly wrong).
         */
    Node p = enq(node);
    int ws = p.waitStatus;
    if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
        LockSupport.unpark(node.thread); // 给线程unpark
    return true;
}

3. ReentrantReadWriteLock与AQS

3.1 特性

  • AQS 的状态state是32位,读锁用高16位,表示持有读锁的线程数(sharedCount),写锁低16位,表示写锁的重入次数 (exclusiveCount)。

  • 状态值为 0 表示锁空闲。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YZNe27oy-1613741042381)(md_imgs/ReentrantReadWriteLock & AQS.png)]

3.2 readLock.lock()

看完源码好像只有叫醒了队列中的后继节点,readLock可以直接(接下

public final void acquireShared(int arg) {
    if (tryAcquireShared(arg) < 0)
        doAcquireShared(arg);
}

接上)因为有读线程持有锁而直接跳过判断,返回业务代码直接执行。

  • 此场景只适用于已经有读线程持有锁,后续读线程来到的时候可以直接跳过判断。

  • 但对于读线程已经在等待队列中,就通过共享锁的设置叫醒后继节点,但是前一个节点还没执行结束,就已经把它挤出了队列,只要反正只要已经被挤出的线程能调用unlock方法就行。

  • 下一个节点也是如此直接把自己设置为头结点,挤出上一个头结点,然后继续往下唤醒。一直唤醒到排它锁节点,就得让刚刚挤出的所有线程执行完毕才可以继续执行排他线程代码。

在这里插入图片描述

3.3 小实验

/*
 * @Author 郭学胤
 * @University 深圳大学
 * @Description
 * @Date 2021/2/8 19:28
 */

import java.util.Random;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class L34_ReadWriteLock {

    static ReentrantLock reentrantLock = new ReentrantLock();
    static ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    static int num;
    static Lock readLock = readWriteLock.readLock();
    static Lock writeLock = readWriteLock.writeLock();

    public static void main(String[] args) throws InterruptedException {
        L34_ReadWriteLock test = new L34_ReadWriteLock();
        /* 使用读写锁,读线程可以同时进行 */
        Runnable task1 = () -> test.read(readLock);
        Runnable task2 = () -> test.write(writeLock, new Random().nextInt(50));

        /*
        * 首先启动4个读线程,每个读线程都是 exclusive
        * 每个读线程睡1s钟,三个执行完需要 4 秒钟
        *
        * E -> E -> E -> E
        * 队列状态如上所示
        * */
        createWriteThread(task2, 4, "write");
        /*
        * 主线程睡1s,保证现在是读线程在占着锁
        *
        * */
        Thread.sleep(1000);
        /*
        * 启动 10 个读线程,因为此时是 写线程在占用锁,
        * 所以读线程已经加到了队列中
        * 此时前边三个写线程全部执行完至少还剩 3s
        *
        * E -> E -> E -> R -> ... -> R
        * */
        createReadThread(task1, 10, "read");
        Thread.sleep(1000);
        /*
        * 再启动 3 个 写线程,前三个中的某个写线程还没有释放锁
        * 所以这三个线程也会加入队列
        * 此时前边三个写线程全部执行完至少还剩 2s
        *
        * E -> E -> E -> R -> ... -> R -> E -> E -> E
        * */
        createWriteThread(task2, 3, "write");
        /*
         * 主线程睡1s,保证现在是读线程在占着锁
         * 此时前边三个写线程全部执行完至少还剩 1s
         * */
        Thread.sleep(1000);
        /*
         * 启动 10 个读线程,因为此时是 写线程在占用锁,
         * 所以读线程已经加到了队列中
         *
         * E -> E -> E -> R -> ... -> R -> E -> E -> E -> R -> R ....
         * */
        createReadThread(task1, 10, "read");

        /*
        * 最后的执行顺序,请大家自己打印看
        * 前四个 写锁
        * 十个 读锁
        * 三个 写锁
        * 十个 读锁
        * */
    }



    public void read(Lock lock) {
        try {
            lock.lock();
            Thread.sleep(1000);
            System.out.println("read over");
        } catch (Exception e) {
        } finally {
            lock.unlock();
        }
    }

    public void write(Lock lock, int val) {
        try {
            lock.lock();
            Thread.sleep(1000);
            num = val;
            System.out.println("write over");
        } catch (Exception e) {
        } finally {
            lock.unlock();
        }
    }


    public static void createWriteThread(Runnable task2, int i2, String write) {
        for (int i = 0; i < i2; i++) {
            new Thread(task2, write + i).start();
        }
    }

    public static void createReadThread(Runnable task1, int i2, String read) {
        for (int i = 0; i < i2; i++) {
            new Thread(task1, read + i).start();
        }
    }
}

实验结论分析

结论一
  • 每次读锁获得锁之后并不是把队列中所有的读锁unpark
  • 读锁变成头结点之后获取到锁之后,unpark下一个读锁的节点
  • 下一个读锁的节点获取锁之后,把之前头结点挤出队列,把自己设置为头节点,然后继续往下unpark下一个读锁节点
  • 当下一个是写锁节点的时候,仍然unpark下一个节点,但是写锁节点暂时获取不到锁资源,需要等待所有的读锁释放资源
结论二
  • 非公平锁只是说新来的线程,仍在自旋中的线程可以尝试获取锁
  • 一旦加到了队列中,就算是非公平锁也仍然需要前驱节点unpark

3.4 源码阅读

3.4.1 readLock.lock()

// 读锁获取,共享锁
 public void lock() {
     sync.acquireShared(1);
 }

private void doAcquireShared(int arg) {
    // 先给自己注册共享模式的节点
    final Node node = addWaiter(Node.SHARED);
    boolean interrupted = false;
    try {
        for (;;) {
            // 获取前一个节点是否是头结点,如果前一个节点时头节点
            final Node p = node.predecessor();
            if (p == head) {
                // 开始获取共享锁
                // 如果获取共享锁成功就开始
                int r = tryAcquireShared(arg);
                if (r >= 0) {
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    return;
                }
            }
            // unpark之后,继续for
            if (shouldParkAfterFailedAcquire(p, node))
                interrupted |= parkAndCheckInterrupt();
        }
    } catch (Throwable t) {
        cancelAcquire(node);
        throw t;
    } finally {
        if (interrupted)
            selfInterrupt();
    }
}


protected final int tryAcquireShared(int unused) {
    
    Thread current = Thread.currentThread();
    int c = getState();
    // 先查看当前有无排它锁,有的话直接返回 -1,说明获取共享锁失败
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    // 没有排它锁,获取当前共享锁数量,并往共享锁的数量上 + 1
    int r = sharedCount(c);
    if (!readerShouldBlock() &&
        r < MAX_COUNT &&
        compareAndSetState(c, c + SHARED_UNIT)) {
        if (r == 0) {
            // 如果没有读锁,自己就是第一个读锁
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            // 读锁重入
            firstReaderHoldCount++;
        } else {
            // 如果第一个读线程不是自己,现在就要给总读线程数上+1了
            HoldCounter rh = cachedHoldCounter;
            if (rh == null ||
                rh.tid != LockSupport.getThreadId(current))
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                readHolds.set(rh);
            rh.count++;
        }
        // 此时就已经获取到了共享读锁
        return 1;
    }
    return fullTryAcquireShared(current);
}

private void setHeadAndPropagate(Node node, int propagate) {
    // 把自己设置为头结点,然后查看自己后边的节点是否是共享模式
    Node h = head; // Record old head for check below
    setHead(node);

    if (propagate > 0 || h == null || h.waitStatus < 0 ||
        (h = head) == null || h.waitStatus < 0) {
        Node s = node.next;
        if (s == null || s.isShared())
            // 如果是共享模式,unpark后边的线程
            doReleaseShared();
    }
}


private void doReleaseShared() {
    for (;;) {
        //唤醒操作由头结点开始,注意这里的头节点已经是上面新设置的头结点了
        //其实就是唤醒上面新获取到共享锁的节点的后继节点
        Node h = head;
        if (h != null && h != tail) {
            int ws = h.waitStatus;
            //表示后继节点需要被唤醒
            if (ws == Node.SIGNAL) {
                //这里需要控制并发,因为入口有setHeadAndPropagate跟releaseShared两个,避免两次unpark
                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                    continue;      
                //执行唤醒操作      
                unparkSuccessor(h);
            }
            //如果后继节点暂时不需要唤醒,则把当前节点状态设置为PROPAGATE确保以后可以传递下去
            else if (ws == 0 &&
                     !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                continue;                
        }
        //如果头结点没有发生变化,表示设置完成,退出循环
        //如果头结点发生变化,比如说其他线程获取到了锁,为了使自己的唤醒动作可以传递,必须进行重试
        if (h == head)                   
            break;
}

获取state的值,查看有无排它锁

有排它锁
  • 自旋等待
  • 创建一个共享模式节点用CAS操作添加到等待队列末尾
  • 调用LockSupport.park()阻塞线程
无排它锁
  • 共享锁数量+1,继续执行代码(或者直接跳出刚刚那个判断语句,直接开始执行代码)

4.2 readLock.unlock()

  • 普通读线程执行的之后,把state数量减-1

  • 最后一个读线程退出的时候,激活等待队列中的线程(此时队列中第一个肯定是写线程

4.3 writeLock.lock()

没有读线程 + 无读线程

setExclusiveOwnerThread为自己

有其他读线程

把自己加入到等待队列中,并且锁模式设置为排它锁

4.4 writeLock.unlock()

setExclusiveOwnerThreadnull,更新state数值

3. CountDownLatchAQS

state为门栓,当门栓为0时,表示线程不用被阻塞可以继续运行。

在这里插入图片描述

3.1 await()

  • 获取state是否为0,为0则可以直接继续执行,不用阻塞当前线程

  • 如果获取到当前门栓值大于0,阻塞当前线程

    • 创建一个共享锁模式的Node,用CAS操作添加到等待队列队尾

    • 调用LockSupport.park()阻塞线程

    • 更新等待队列的头结点为刚刚创建的节点,并把头结点的waitStatus设置为propgate

3.2 countDown()

  • CAS操作更新state的值,因为值大于0导致其他线程阻塞,所以在每次调用 state--

  • 当最后把state更新为0的时候,unparkSuccessor,解除头结点封印,又因为各个线程都是共享锁,所以一旦头结点执行,所有的等待队列中的线程均可以激活,周而复始的把自己设置为头结点,然后挤出原有的头结点

在这里插入图片描述

5. Varhandle

  • 普通属性进行原子操作
  • 比反射快,直接操纵二进制码
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值