【JUC源码】JUC核心:AQS(一)底层结构分析

AQS 系列:

首先,从类注释可以得到的信息:

  1. 提供了一种框架,自定义了先进先出的同步队列,让获取不到锁的线程能进入同步队列中排队;
  2. 同步器有个状态字段,我们可以通过状态字段来判断能否得到锁,此时设计的关键在于依赖安全的 atomic value 来表示状态(虽然注释是这个意思,但实际上是通过把状态声明为 volatile,在锁里面修改状态值来保证线程安全的);
  3. 子类可以通过给状态 CAS 赋值来决定能否拿到锁,可以定义那些状态可以获得锁,哪些状态表示获取不到锁(比如定义状态值是 0 可以获得锁,状态值是 1 就获取不到锁);
  4. 子类可以新建非 public 的内部类,用内部类来继承 AQS,从而实现锁的功能;
  5. AQS 提供了排它模式和共享模式两种锁模式。排它模式下:只有一个线程可以获得锁,共享模式可以让多个线程获得锁,子类 ReadWriteLock 实现了两种模式;
  6. 内部类 ConditionObject 可以被用作 Condition,我们通过 new ConditionObject () 即可得到条件队列;
  7. AQS 实现了锁、排队、锁队列等框架,至于如何获得锁、释放锁的代码并没有实现,比如 tryAcquire、tryRelease、tryAcquireShared、tryReleaseShared、isHeldExclusively 这些方法,AQS 中默认抛 UnsupportedOperationException 异常,都是需要子类去实现的;
  8. AQS 继承 AbstractOwnableSynchronizer 是为了方便跟踪获得锁的线程,可以帮助监控和诊断工具识别是哪些线程持有了锁;
  9. AQS 同步队列和条件队列,获取不到锁的节点在入队时是先进先出,但被唤醒时,可能并不会按照先进先出的顺序执行。

AQS 的注释还有很多很多,以上 9 点是比较重要的注释总结。


AbstractQueuedSynchronizer 继承关系,核心成员变量,主要构造函数:

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {

    // 同步器的状态,根据当前状态进行判断是否可以获得当前锁
    // 如果当前state是0,那么可以获得锁
    // 可重入锁,每次获得锁+1,每次释放锁-1
    private volatile int state;

    // 自旋超时阀值,单位纳秒
    // 当设置等待时间时才会用到这个属性
    static final long spinForTimeoutThreshold = 1000L;
    
     // 同步队列的头
    private transient volatile Node head;

    // 同步队列的尾
    private transient volatile Node tail;
    
    // Node
    // 即使同步队列的Node,也是条件队列的Node
    static final class Node {...}
    
    // 条件队列,从基础属性上可以看出是链表队列
    public class ConditionObject implements Condition, java.io.Serializable
    
    //----------------------------------Node------------------------------------------------------
    // 静态内部类,既是同步队列的节点,也是条件队列的节点
    // 注:从 next、prev 可以看出同步队列和条件队列的实现都是双向链表(条件队列的head、tail在上面已经定义了)
	static final class Node {
    
	    // 当前节点的线程
	    volatile Thread thread;
	   
	    // 当前节点的前节点
	    // 节点 acquire 成功后就会变成head
	    // head 节点不能被 cancelled
	    volatile Node prev;
	
	    // 当前节点的下一个节点
	    volatile Node next;
	
	    /**
	     * 两个队列共享的属性
	     * 注:AQS 的 state 决定能不能拿锁,node 的状态影响哪个线程能拿锁
	     */
	    // 表示当前节点的状态,通过节点的状态来控制节点的行为
	    // 普通同步节点就是 0 ,实际一共5种状态(1,0,-1,-2,-3)
	    volatile int waitStatus;
	
	    // 被取消,在同步队列中出错时会被(暂时)置为cancel,随后就会删除
	    static final int CANCELLED =  1;
	    // 表示其后有待唤醒的节点,存在的意义是防止有节点被忘记唤醒
	    static final int SIGNAL    = -1;
	    // 表示当前 node 正在条件队列中,当有节点从同步队列转移到条件队列时,状态就会被更改成 CONDITION
	    static final int CONDITION = -2;
	    // 无条件传播,共享模式下,该状态的进程处于可运行状态
	    static final int PROPAGATE = -3;
	
	    // 在同步队列中,nextWaiter 只是表示当前 Node 是排它模式还是共享模式
	    // 在条件队列中,nextWaiter 就是表示下一个节点元素
	    Node nextWaiter;
	    
	    // node是共享模式
	    static final Node SHARED = new Node();
	    // node是排它模式
	    static final Node EXCLUSIVE = null;
	}

	//----------------------------------ConditionObject-----------------------------------------
	// 条件队列,从属性上可以看出也是双向链表
	// 注:关于命名,条件队列中将 node 叫做 waiter
	public class ConditionObject implements Condition, java.io.Serializable {
	    private static final long serialVersionUID = 1173984872572414699L;
	    // 条件队列中第一个 node
	    private transient Node firstWaiter;
	    // 条件队列中最后一个 node
	    private transient Node lastWaiter;
	}  
}

我们先来看看 AQS 继承的 AbstractOwnableSynchronizer,AbstractOwnableSynchronizer 的作用就是为了知道当前是那个线程获得了锁,方便监控用

Condition 接口

我们再来看一下条件队列 ConditionObject 实现的 Condition 接口:

public interface Condition {
    // 如果线程被打断,抛 InterruptedException 异常
    void await() throws InterruptedException;
    
    // 虽然入参可以是任意的时间,但底层仍然转化成纳秒
    boolean await(long time, TimeUnit unit) throws InterruptedException;
    
    // 返回的 long 值表示剩余的给定等待时间,如果返回的时间小于等于 0 ,说明等待时间过了
    // 选择纳秒是为了避免计算剩余等待时间时的截断误差
    long awaitNanos(long nanosTimeout) throws InterruptedException;
    
    // 和 wait 方法比较,不能被打断,其余一样
    void awaitUninterruptibly();
    
    // 返回值表示目前为止,指定日期是否到期,true 表示没有过期,false 表示过期了
    boolean awaitUntil(Date deadline) throws InterruptedException;
    
    // 唤醒一个等待的线程,在被唤醒前必须先获得锁
    void signal();
    
    // 唤醒条件队列中的所有线程
    void signalAll();
}

AQS 实现 Condition 接口的作用:

  • 当 lock 代替 synchronized 来加锁时,Condition 就可以用来代替 Object 中相应的监控方法了,比如 Object.wait、Object.notify、Object.notifyAll 这些方法;
  • 提供了一种线程协作方式:一个线程被暂停执行,直到被其它线程唤醒;
  • Condition 实例是绑定在锁上的,通过 Lock#newCondition 方法可以产生该实例;
  • 除了特殊说明外,任意空值作为方法的入参,都会抛出空指针;
  • Condition 提供了明确的语义和行为,这点和 Object 监控方法不同。

AQS 设计模型

AQS 的核心思想是:开源节流,两种模式加锁

  • 开源:所有线程都能进入同步队列
  • 节流:只有极少数线程能运行
  • 两种加锁模式
    • 独占与共享
      • 独占:只有1个线程能执行
      • 共享:1个线程在执行前会叫醒其他线程一起执行
    • 公平与非公平
      • 公平:进入同步队列,服从同步器调度,先入先出
      • 非公平:可以不进入同步队列,CAS修改state成功直接拿锁运行

关于独占、共享、公平、非公平理解一下这张图:

注:独占、共享、公平、非公平都是对于线程获取锁而言的,在锁释放时是没有这些概念的

  •  

猜你喜欢

转载自blog.csdn.net/qq_33762302/article/details/114381077