AbstractQueuedSynchronizer

一、AQS的概念及使用

Java并发编程核心在于 java.concurrent.util 包而juc当中的大多数同步器实现都是围绕着共同的基础行为,比如等待队列、条件队列、独占获取、共享获取等,而这个行为的抽象就是基于 AbstractQueuedSynchronizer 简称AQS,AQS定义了一套多线程访问共享资源的同步器框架,是一个依赖状态(state)的同步器。

子类们必须定义改变state变量的protected方法,这些方法定义了state是如何被获取或释放的。鉴于此,本类中的其他方法执行所有的排队和阻塞机制。子类也可以维护其他的state变量,但是为了保证同步,必须原子地操作这些变量。

AbstractQueuedSynchronizer会把所有的请求线程构成一个CLH队列,当一个线程执行完毕(lock.unlock())时会激活自己的后继节点,但正在执行的线程并不在队列中,而那些等待执行的线程全部处于阻塞状态。
  AQS是一个同步器,设计模式是模板模式。核心数据结构:双向链表 + state(锁状态);底层操作:CAS

Java.concurrent.util当中同步器的实现如Lock,Latch,Barrier 等,都是基于AQS框架实现 ;

一般通过定义内部类Sync继承AQS ;
将同步器所有调用都映射到Sync对应的方法;
ReentrantLock主要方法:

lock()获得锁
lockInterruptibly()获得锁,但优先响应中断
tryLock()尝试获得锁,成功返回true,否则false,该方法不等待,立即返回
tryLock(long time,TimeUnit unit)在给定时间内尝试获得锁
unlock()释放锁
Condition:await()、signal()方法分别对应之前的Object的wait()和notify()

和重入锁一起使用
await()是当前线程等待同时释放锁
awaitUninterruptibly()不会在等待过程中响应中断
signal()用于唤醒一个在等待的线程,还有对应的singalAll()方法

二、AQS源码分析

AbstractQueuedSynchronizer内部结构查看:https://www.processon.com/view/link/60890e8a1e08531350533a05#map

我们以 ReentrantLock(独占锁)作为切入点来学习 AbstractQueuedSynchronizer;

1、ReentrantLock 和 AbstractQueuedSynchronizer 的部分代码
ReentrantLock的内部类 Sync 继承了 AbstractQueuedSynchronizer 类, Sync的两个子类又实现了非公平锁和公平锁;

class ReentrantLock {
    class Sync extends AbstractQueuedSynchronizer{
        abstract void lock(); //加锁, 子类需求重写该方法
    }

    //非公平锁, 默认
    class NonfairSync extends Sync {
       //省略 lock()的实现
    }
    
    //公平锁
    static final class FairSync extends Sync {
        //省略 lock()的实现
    }
}

2、锁实现(加锁 Lock.lock())

非公平锁:无论CLH队列中是否有节点,当前线程都要和队列头的节点去竞争一下锁;若竞争到锁,则该线程去持有锁;若没有竞争到锁,则放入到CLH队列尾部;

公平锁:无论CLH队列中是否有节点,当前线程都是去放到队列的尾部

2.1 非公平锁实现

static final class NonfairSync extends Sync {
    final void lock() {
        if (compareAndSetState(0, 1)) //用CAS算法尝试获取锁
            setExclusiveOwnerThread(Thread.currentThread());  //当前线程占用锁
        else
            acquire(1);
    }
}

(1)若没有加锁(state == 0),则直接让当前线程占有锁;
(2)若已经加锁了(state > 0),则执行 AbstractQueuedSynchronizer.acquire(int arg)方法

Sync.nonfairTryAcquire
该方法主要是去尝试获取锁(加锁)

final boolean nonfairTryAcquire(int acquires) {
    //acquires = 1
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
        if (compareAndSetState(0, acquires)) { //unsafe操作,cas修改state状态
            setExclusiveOwnerThread(current); //独占状态锁持有者指向当前线程
            return true;
        }
    }
    else if (current == getExclusiveOwnerThread()) { //state状态不为0,锁持有者是当前线程,则state+1
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false; //加锁失败
}

a. 首先判断当前状态,若 c==0 说明没有线程占用该锁,并在占用锁成功之后将锁指向当前线程;

b. 如果 c != 0 说明有线程正拥有了该锁,而且若占用该锁就是当前线程(锁重入),则将 state 加 1;这段的代码只是简单地++acquires,并修改status值,是因为没有竞争获取锁的本身就是当前线程,所以通过setStatus修改state,而非CAS。

3、解锁( Lock.unlock() )

Sync.tryRelease 方法

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值