AQS原理
AQS是一个用来创建锁和同步器的框架,使用AQS能简单且高效的构造出应用广泛的大量的同步器;比如我们提到的RenentranLock,Semphore。,其他的诸如ReentrantReadWriteLock,synchronousQueue,FutureTask等等皆是基于AQS的,当然,我们自己也能利用AQS非常轻松容易地构造出符合自己同步需要需求的同步器;
AQS原理概览;
AQS核心思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的共享线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是CLH队列锁实现的;即将暂时获取不到的锁加入到队列中;
CLH队列是一个虚拟的双向队列(虚拟的双向队列即不存在队列实例,仅存在结点之间的联系)。AQS是将每条请求共享资源的线程封装成一个CLH锁队列的一个结点来实现锁的分配;
AQS对资源的共享方式
AQS定义两种资源共享方式
EXclusive:(独占)只有一个线程能执行;如reentrantlock。可分为公平锁和非公平锁;
公平锁:按照线程在队列中的排队顺序;先到者拿到锁;
非公平锁:当线程要获取锁,无视队列顺序直接去拿锁;谁抢到就是谁的;
Share:(共享)多个线程可同时执行,如Semaphone/CountdownLatch。
AQS底层使用了模板方法模式;
同步器的设计方法是基于模板方法模式,如果需要自定义同步器一般的方式是这样,
1. 使用者继承AbastractSynchronzier并重写制定的方法;
2. 将AQS组合在自定义同步组件的实现中,并调用其模板方法,而这些模板方法会调用使用者重写的方法;
需要重写的方法:
isHeldExclusively()://该线程是否正在独占资源;只有用到condition才需要去实现它;
tryAcquire()://独占方式,尝试获取资源,成功则返回true,失败则返回false;
tryReLease()://独占方式,尝试释放资源,成功则返回true,失败则返回false;
tryAcquireShared(int)://共享方式,尝试获取资源,负数表示失败,0表示成功,但没有剩余可用资源,整数表示成功,且有剩余资源;
tryReleaseShared(int):////共享方式,尝试释放资源,负数表示失败,0表示成功,但没有剩余可用资源,整数表示成功,且有剩余资源;
默认情况下,每个方法都抛出UnSupportedOperationException,这些方法的实现必须是内部线程安全的,并且通常简短而不是应该阻塞,AQS类中的其他方法都final;所以无法被其他类使用,只有这几个方法可以被其他类使用;
以Renentrantlock为例,state初始化为0;表示未锁定状态,A线程lock()时,会调用tryAcquire()独占该锁并将state+1;此后,其他线程tryAcquire()时就会失败,直到A线程unlock到state=0(即释放锁位)为止,其他线程才有机会获取该锁,当然,释放锁之前,A线程是可以重复获取此锁的(state会累加),这就是可重入的概念,但要注意,获取多少次就要释放多少次,这样才能保证state是能回到零态的;
再以countdownLatch为例,任务分为N个子线程去执行,state也初始化为N(注意N要与线程个数一致)。这N个子线程是并行执行的,每个子线程执行后cutdown()一次,state会CAS减1,等到所有子线程都执行完后(即state=0),会unpark()主调用线程,然后主调用线程就会从await()函数返回,继续后动作;
一般自定义同步器要么是独占方法,要么是共享方式,他们只需实现try-acquire-tryrealse、tryAcquireShared-tryRelaseShared的一种即可;但AQS也支持自定义同步器同时实现独占和共享两种方式:如reentrantwriteLock。
文章参考自:https://github.com/Snailclimb