Java并发包(JUC)中提供了很多并发工具,譬如ReentrangLock、Semaphore,它们的实现都用到了一个共同的基类–AbstractQueuedSynchronizer,简称AQS。
AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器。
那么今天我们就来简单的谈一谈AQS。
基本实现原理
AQS使用一个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作。
private volatile int state; //共享变量,使用volatile修饰保证线程可见性
这样状态信息通过procted类型的getState,setState,compareAndSetState等方法进行操作。
AQS为使用提供了底层支撑,如何组装实现,使用者可以自由发挥。
同步器的设计是基于模板方法模式的,一般的使用方式是这样:
1.使用者继承AbstractQueuedSynchronizer并重写指定的方法。(这些重写方法很简单,无非是对于共享资源state的获取和释放)
2.将AQS组合在自定义同步组件的实现中,并调用其模板方法,而这些模板方法会调用使用者重写的方法。
这其实是模板方法模式的一个很经典的应用。
我们来看看AQS定义的这些可重写的方法:
protected boolean tryAcquire(int arg) //独占式获取同步状态,试着获取,
// 成功返回true,反之为false
protected boolean tryRelease(int arg) //独占式释放同步状态,等待中的其他线程此时
// 将有机会获取到同步状态;
protected int tryAcquireShared(int arg) //共享式获取同步状态,返回值大于等于0,
//代表获取成功;反之获取失败;
protected boolean tryReleaseShared(int arg) //共享式释放同步状态,成功为true,失败为false
protected boolean isHeldExclusively() // 是否在独占模式下被线程占用。
综上所述,我们来总结一下AQS的使用:
首先,我们需要去继承AbstractQueuedSynchronizer这个类,然后我们根据我们的需求去重写相应的方法,比如要实现一个独占锁,那就去重写tryAcquire,tryRelease方法,要实现共享锁,就去重写tryAcquireShared,tryReleaseShared;最后,在我们的组件中调用AQS中的模板方法就可以了,而这些模板方法是会调用到我们之前重写的那些方法的。
下面我们用一个简单的例子来看看如何使用AQS:
Mutex类:
public class Mutex implements java.io.Serializable {
9 //静态内部类,继承AQS
10 private static class Sync extends AbstractQueuedSynchronizer {
11 //是否处于占用状态
12 protected boolean isHeldExclusively() {
13 return getState() == 1;
14 }
15 //当状态为0的时候获取锁,CAS操作成功,则state状态为1,
16 public boolean tryAcquire(int acquires) {
17 if (compareAndSetState(0, 1)) {
18 setExclusiveOwnerThread(Thread.currentThread());
19 return true;
20 }
21 return false;
22 }
23 //释放锁,将同步状态置为0
24 protected boolean tryRelease(int releases) {
25 if (getState() == 0) throw new IllegalMonitorStateException();
26 setExclusiveOwnerThread(null);
27 setState(0);
28 return true;
29 }
30 }
31 //同步对象完成一系列复杂的操作,我们仅需指向它即可
32 private final Sync sync = new Sync();
33 //加锁操作,代理到acquire(模板方法)上就行,acquire会调用我们重写的tryAcquire方法
34 public void lock() {
35 sync.acquire(1);
36 }
37 public boolean tryLock() {
38 return sync.tryAcquire(1);
39 }
40 //释放锁,代理到release(模板方法)上就行,release会调用我们重写的tryRelease方法。
41 public void unlock() {
42 sync.release(1);
43 }
44 public boolean isLocked() {
45 return sync.isHeldExclusively();
46 }
47 }
TestMutex.java:
public class TestMutex {
9 private static CyclicBarrier barrier = new CyclicBarrier(31);
10 private static int a = 0;
11 private static Mutex mutex = new Mutex();
12
13 public static void main(String []args) throws Exception {
14 //说明:我们启用30个线程,每个线程对i自加10000次,同步正常的话,最终结果应为300000;
15 //未加锁前
16 for(int i=0;i<30;i++){
17 Thread t = new Thread(new Runnable() {
18 @Override
19 public void run() {
20 for(int i=0;i<10000;i++){
21 increment1();//没有同步措施的a++;
22 }
23 try {
24 barrier.await();//等30个线程累加完毕
25 } catch (Exception e) {
26 e.printStackTrace();
27 }
28 }
29 });
30 t.start();
31 }
32 barrier.await();
33 System.out.println("加锁前,a="+a);
34 //加锁后
35 barrier.reset();//重置CyclicBarrier
36 a=0;
37 for(int i=0;i<30;i++){
38 new Thread(new Runnable() {
39 @Override
40 public void run() {
41 for(int i=0;i<10000;i++){
42 increment2();//a++采用Mutex进行同步处理
43 }
44 try {
45 barrier.await();//等30个线程累加完毕
46 } catch (Exception e) {
47 e.printStackTrace();
48 }
49 }
50 }).start();
51 }
52 barrier.await();
53 System.out.println("加锁后,a="+a);
54 }
55 /**
56 * 没有同步措施的a++
57 * @return
58 */
59 public static void increment1(){
60 a++;
61 }
62 /**
63 * 使用自定义的Mutex进行同步处理的a++
64 */
65 public static void increment2(){
66 mutex.lock();
67 a++;
68 mutex.unlock();
69 }
70 }
最后的运行结果:
加锁前,a=279204
加锁后,a=300000