AbstractQueuedSynchronizer源码解析

本文详细解读了AbstractQueuedSynchronizer(AQS)的源码,包括其类结构、数据结构、获取/释放锁的逻辑以及ConditionObject的等待/唤醒功能。AQS作为Java多线程同步的基础,使用链表结构管理线程,并通过CAS操作保证线程安全。文章通过实例解析了acquire和release方法,阐述了等待队列的工作原理,以及await和signal方法如何实现线程的等待与唤醒。
摘要由CSDN通过智能技术生成

t4517591789011968

AbstractQueuedSynchronizer源码

AbstractQueuedSynchronizer通常简称AQS,他是java.util.locks包下的一个类,Java中的很多类都是通过改类实现的。

在本文中我们通过阅读源码的方式查看下其实现原理,使用的JDK版本为1.8.

1、类结构与数据结构

1.1 类继承关系

image-20210527153832062

AbstractQueuedSynchronizer继承AbstractOwnableSynchronizerAbstractOwnableSynchronizer的定义是比较简单的,里面只有一个字段exclusiveOwnerThread,该字段是用来存储当前占有同步器的的线程。

1.2 成员变量

AQS类中的成员变量如下:

  • Node head 头节点
  • Node tail 尾节点
  • int state 状态值 通过对该字段的操作实现同步的逻辑

AQS中有个ConditionObject内部类,其成员变量如下

  • Node fitstWaiter 头节点
  • Node lastWaiter 尾节点

1.3 数据结构

通过上面的成员变量,我们很容易想到这是一个链表结构,链表节点的定义如下:

static final class Node {
   
    // 共享锁模式的nextWaiter
    static final Node SHARED = new Node();
	// 独占锁模式的nextWaiter
    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;

    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() {
   
    }

    Node(Thread thread, Node mode) {
   
        this.nextWaiter = mode;
        this.thread = thread;
    }

    Node(Thread thread, int waitStatus) {
   
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

通过节点的定义我们发现里面双向链表和单向链表同时存在,这两个链表分别有什么用我们会在后面的源码学习中一点点的讲解。其结构如下图所示:

image-20210603192148366

节点的状态有如下几种,我们会在后面的源码查看中了解什么时候会设置成相应的值:

  • CANCELLED = 1
  • SIGNAL = -1
  • CONDITION = -2
  • PROPAGATE = -3
  • 0

2 获取/释放锁

在上面的部门我们了解了AQS的数据结构,在这部分中我们看下AQS是如何处理获取锁和释放锁的逻辑的。

AQS中存在独占和共享两种模式,对外提供的获取/释放的方法如下:

  • protected final boolean compareAndSetState(int expect, int update) 通过CAS的方式修改state属性
  • public final void acquire(int arg) 获取排他锁
  • public final void acquireShared(int arg) 获取共享锁
  • public final boolean tryAcquireNanos(int arg, long nanosTimeout) 带超时时间的获取排他锁
  • public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) 带超时时间的获取共享锁
  • public final boolean release(int arg) 释放排他锁
  • public final boolean releaseShared(int arg) 释放共享锁

2.1 compareAndSetState方法

该方法是线程安全的修改state属性的值,其使用的是Unsafe类的CAS操作,其源码如下

protected final boolean compareAndSetState(int expect, int update) {
   
    // 使用Unsafe类进行CAS操作,保证线程安全
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

2.2 acquire方法

该方法是排他锁获取锁的方法,其源码如下

public final void acquire(int arg) {
   
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        // 获取锁失败且加入等待队列失败,执行Thread.interrupt()方法
        selfInterrupt();
}

在这个方法中会调用tryAcquire方法、addWaiter方法和acquireQueued方法。

tryAcquire方法是尝试获得锁,阅读tryAcquire方法的源码我们会发现在AQS中直接抛出了异常,很明显这里使用了方法模板模式,需要子类来实现该方法,其源码如下:

protected boolean tryAcquire(int arg) {
   
    // 在AQS中直接抛出异常,留给子类实现   模板方法
    throw new UnsupportedOperationException();
}

如果获取锁失败,AQS会将该线程添加到等待队列,addWaiter方法源码如下

private Node addWaiter(Node mode) {
   
    // 创建一个节点对象
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值