从Condition开始,回顾AQS

Synchronized和Reentrantlock的挂起逻辑

synchronized中有两个核心的结构

  • EntryList + cxq:等待拿锁的线程存储位置
  • Waitset:被执行wait方法的线程存储位置

流转: 

  • 线程获取锁资源失败,扔到EntryList + cxq
  • 线程持有锁资源,执行了wait方法,扔到WaitSet
  • 其他线程执行了notify/notifyAll方法,WaitSet中的线程会扔到EntryList/cxq中

ReetrantLock中的Condition中支持了类似上述的功能

ReetrantLock中有两个核心的结构

  • AQS的同步队列:等待拿锁的线程存储位置
  • AQS内部的Condition单向链表:被执行await方法的线程存储位置

流转: 

  • 线程获取锁资源失败,扔到同步队列
  • 线程持有锁资源,执行了await方法,扔到Condition单向链表
  • 其他线程执行了signal/signalAll方法,WaitSet中的线程会扔到同步队列中

AQS的Condition支持

 AQS是JUC包下的一个抽象类,单独聊AQS没什么,但是他是AQS很多JUC包下的工具类的父类

AQS有三个核心点:

  • int类型的state属性
  • AQS内部的同步队列
  • Condition的单向链表

本文主要细看Condition单向链表:

static final class Node {
    static final Node SHARED = new Node();

    static final Node EXCLUSIVE = null;

    static final int CANCELLED =  1;

    static final int SIGNAL    = -1;

    static final int CONDITION = -2;

    static final int PROPAGATE = -3;

    /*
        Node只要在Condition单向链表中,状态就是上面的-2
        waitStatus简写wt
     */
    volatile int waitStatus;

    volatile Node prev;

    volatile Node next;

    volatile Thread thread;

    /*
        单向链表的下一个节点
     */
    Node nextWaiter;

    final boolean isShared() {
        return nextWaiter == SHARED;
    }

    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }

    Node() {    // Used to establish initial head or SHARED marker
    }

    Node(Thread thread, Node mode) {     // Used by addWaiter
        this.nextWaiter = mode;
        this.thread = thread;
    }

    Node(Thread thread, int waitStatus) { // Used by Condition
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

  • Condition是基于Node对象组成的单向链表 
  • 在Condition中,Node状态必须是-2,如果不是-2,就可以从中移除掉了
  • Condition的Node是利用nextWaiter属性连接下一个节点
  • Condition中还有指向头尾的两个属性,分别是firstWaiter和lastWaiter

Condition的挂起操作流程

当持有lock锁的线程,执行以下4个流程:

  1. 将当前对象封装成Node对象,加入单向链表中
  2. 释放锁资源
  3. 确认当前线程的Node,没有在AQS的同步队列中。如果在,说明执行了signal方法,那个线程已经进入了同步队列。不需要挂起
  4. 没有在同步队列,直接挂起

 

Condition的signal唤醒操作流程

  1. 确保执行signal的线程持有锁资源
  2. 将第一个Node从单向链表中断开
  3. 将Node的状态从-2改成0
  4. 将Node移到同步队列
  5. 确保Node在同步队列中可以被唤醒。直接唤醒线程和将prev指向的Node状态设置为-1

 

Condition在await被唤醒后的逻辑

1、确认被唤醒的方式:

  • 单纯地被signal方法唤醒
  • 被interrupt中断唤醒
  • 被signal唤醒后,然后执行了interrupt(保留中断标记位)

2、确保Node在同步队列后,就可以跳出while循环

3、执行acquireQueued方法后,等待获取锁资源

4、在获取锁资源的同时,如果被中断过,需要确认是否保留中断标记位

5、如果是中断唤醒,需要将当前Node断开单向链表连接

6、根据中断模型,执行抛出异常、方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值