AQS浅谈

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值