AQS原理简单阐述

1. AQS

AQS全称(AbstractOwnableSynchronizer),在包package java.util.concurrent.locks下。

在这里插入图片描述

/**
 * Provides a framework for implementing blocking locks and related
 * synchronizers (semaphores, events, etc) that rely on
 * first-in-first-out (FIFO) wait queues.  This class is designed to
 * be a useful basis for most kinds of synchronizers that rely on a
 * single atomic {@code int} value to represent state. 
 */

从源码中的注释可以看出 AQS是一个框架用来构建阻塞锁和基于等待队列的同步器,同时它使用了一个原子变量来代表状态

在这里插入图片描述

查看继承结构可以看出,CountDownLatch,ReentrantLock…都是通过AQS实现的。

2. 原理

AQS的核心思想是:如果 被 请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并将资源的状态设置为锁定状态。如果 被 请求的资源被占用,就需要线程阻塞等待以及被唤醒的 锁分配机制。

AQS使用CLH队列锁实现(自旋锁),将无法获取资源的线程加入到队列

CLH是一个虚拟的双向队列, 并不是真实存在的队列,只有一个个的节点,定义节点之间的关系来实现队列的特性。

CLH会把请求的线程封装成一个个的节点加入到队列中。

自旋锁:如果请求线程无法获取共享资源,自旋锁已经被别的线程持有了,线程不会进入休眠状态,而是循环等待 占有自旋锁的线程释放锁。

在这里插入图片描述
图片摘自

队列中的节点

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;
	// 表示当前节点在condition上
    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;
    }
}

怎么判断资源是否被占用呢?

AQS使用一个 int 变量来表示同步: 使用 CAS 和 Volatile 来实现同步

// volatile 保证内存的可见性
private volatile int state;

protected final int getState() {
        return state;
}

protected final void setState(int newState) {
    state = newState;
}
// CAS操作(将state设置为updata,前提是当前state的值为expect)
protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

资源的占有方式

AQS定义的两种资源的占有方式:

  1. EXCLUSIVE (独占): 只有一个线程可以占有资源。比如: ReentrantLock

    • 又可以分为公平锁和非公平锁:

      • 公平锁: 按照线程在队列中的顺序先到先得

      • 非公平锁:无视线程在队列中的顺序,谁抢到就是谁的

  2. SHARE(共享): 多个线程可以占有资源。比如:Semaphore,CountDownLatch,CyclicBarrier,ReadWriteLock

AQS使用了模板模式

当我们需要创建自定义的同步器时只需要简单的步骤:

  1. 继承AQS类,并重写指定的方法。

    ​ 自定义同步器重写下面这几个模板方法。

    isHeldExclusively()//该线程是否正在独占资源。只有⽤到condition才需要去实现它。
    tryAcquire(int)//独占⽅式。尝试获取资源,成功则返回true,失败则返回false。
    tryRelease(int)//独占⽅式。尝试释放资源,成功则返回true,失败则返回false。
    tryAcquireShared(int)//共享⽅式。尝试获取资源。负数表示失败; 0表示成功,但没有剩余可⽤资源;正数表示成功,且有剩余资源。
    tryReleaseShared(int)//共享⽅式。尝试释放资源,成功则返回true,失败则返回
    false

一般来说,自定义同步器要么是独占式,要不是共享式,可以选择性的重写方法。
但是也支持同时实现独占式和共享式,如读写锁ReentrantReadWriteLock

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值