AbstractQueuedSynchronizer的使用和juc里的相关类的解析

对AQS进行解析后,先来实现两个简单的基于AQS的类,然后再解析juc里基于AQS构造的类。

1、基于AQS的类的示例

首先先看这个类,这个类是《Java并发编程实战》的一个示例,AQS源码的注释里也给了类似的实现。这个类是以共享模式实现的,在调用signal之前,调用await方法的线程都将被阻塞,main方法的示例演示了这种情况。

 1 public class Latch {
 2     private Sync sync = new Sync();
 3 
 4     private class Sync extends AbstractQueuedSynchronizer{
 5         @Override
 6         protected int tryAcquireShared(int arg) {//重写tryAcquireShared方法
 7             return getState() == 1 ? 1 : -1;
 8         }
 9 
10         @Override
11         protected boolean tryReleaseShared(int arg) {//重写tryReleaseShared方法
12             setState(1);
13             return true;
14         }
15     }
16 
17     public void signal(){//调用这个方法会调用releaseShared方法,会调用tryReleaseShared方法,将state设置为0 没调用这个方法之前调用tryAcquireShared的返回值都是0,线程会阻塞
18         sync.releaseShared(0);
19     }
20 
21     public void await() throws InterruptedException {//调用这个方法会调用acquireSharedInterruptibly方法,会调用tryAcquireShared方法,如果返回值大于0,则集训运行;否则阻塞
22         sync.acquireSharedInterruptibly(0);
23     }
24 
25     public static void main(String [] args) throws InterruptedException {
26         Latch latch = new Latch();
27         Runnable runnable = new Runnable() {
28             @Override
29             public void run() {
30                 try {
31                     latch.await();
32                 } catch (InterruptedException e) {
33                     e.printStackTrace();
34                 }
35                 System.out.println("haha");
36             }
37         };
38 
39         new Thread(runnable).start();
40         new Thread(runnable).start();
41 
42         Thread.sleep(5 * 1000);
43 
44         System.out.println("xixi");
45         latch.signal();
46     }
47 }

这个类是AQS注释里给出的一个实现的一部分。这个类是以独占模式实现的。同时只能有一个线程

 1 public class Mock {
 2     private Sync sync = new Sync();
 3 
 4     private class Sync extends AbstractQueuedSynchronizer{
 5         @Override
 6         protected boolean isHeldExclusively() {
 7             return getState() == 1;
 8         }
 9 
10         @Override
11         protected boolean tryAcquire(int acquires) {//重写tryAcquire方法,用cas尝试将state从0置为1
12             assert acquires == 1;
13             if (compareAndSetState(0, 1)){
14                 setExclusiveOwnerThread(Thread.currentThread());//如果将state从0置为1成功,则将当前线程设置为独占模式持有资源的线程
15                 return true;
16             }
17             return false;
18         }
19 
20         @Override
21         protected boolean tryRelease(int releases) {
22             assert releases == 1;
23             if (getState() == 0){//如果state为0,则说明资源没有被占用,不需要释放 抛出一个异常
24                 throw new IllegalMonitorStateException();
25             }
26             setExclusiveOwnerThread(null);//将资源拥有者线程置为null
27             setState(0);//将state置为0,资源释放
28             return true;
29         }
30     }
31 
32     @Test
33     public void lock(){
34         sync.acquire(1);
35     }
36 
37     public boolean tryAcquire(){
38         return sync.tryAcquire(1);
39     }
40 
41     public void unlock(){
42         sync.release(1);
43     }
44 
45     public static void main(String [] args) throws InterruptedException {
46         Mock mock = new Mock();
47         Runnable runnable = new Runnable() {
48             @Override
49             public void run() {
50                 mock.lock();
51                 System.out.println("haha");
52                 try {
53                     Thread.sleep(5 * 1000);
54                 } catch (InterruptedException e) {
55                     e.printStackTrace();
56                 }
57                 mock.unlock();
58             }
59         };
60 
61         new Thread(runnable).start();
62         new Thread(runnable).start();
63 
64         mock.lock();
65         System.out.println("xixi");
66         Thread.sleep(5 * 1000);
67         mock.unlock();
68     }
69 }

2、juc里基于AQS构造的类

Semaphore类:

内部抽象类Sync:

 1     abstract static class Sync extends AbstractQueuedSynchronizer {
 2         private static final long serialVersionUID = 1192457210091910933L;
 3 
 4         Sync(int permits) {
 5             setState(permits);
 6         }
 7 
 8         final int getPermits() {
 9             return getState();
10         }
11 
12         final int nonfairTryAcquireShared(int acquires) {//不公平尝试获取共享锁
13             for (;;) {
14                 int available = getState();
15                 int remaining = available - acquires;
16                 if (remaining < 0 ||
17                     compareAndSetState(available, remaining))//如果剩下的资源不够分配,直接返回,会进入阻塞状态;如果剩下的资源足够分配,自旋直到分配成功,返回分配后剩下的资源
18                     return remaining;
19             }
20         }
21 
22         protected final boolean tryReleaseShared(int releases) {//尝试释放锁
23             for (;;) {//自旋
24                 int current = getState();
25                 int next = current + releases;
26                 if (next < current)//next溢出为负数 抛出异常
27                     throw new Error("Maximum permit count exceeded");
28                 if (compareAndSetState(current, next))//cas修改state
29                     return true;
30             }
31         }
32 
33         final void reducePermits(int reductions) {//减少许可
34             for (;;) {
35                 int current = getState();
36                 int next = current - reductions;
37                 if (next > current) // underflow
38                     throw new Error("Permit count underflow");
39                 if (compareAndSetState(current, next))
40                     return;
41             }
42         }
43 
44         final int drainPermits() {//将许可清空
45             for (;;) {
46                 int current = getState();
47                 if (current == 0 || compareAndSetState(current, 0))
48                     return current;
49             }
50         }
51     }

内部类NonfairSync:

这个类继承自抽象类Sync,实现了tryAcquireShared方法,由类名可知这个类是非公平获取资源的(非公平就是说一个线程尝试获取资源时,就算有其它线程在排队获取资源,但是由于这些资源处于等待状态,会让新的线程先尝试获取资源,如果直接能获取就让新线程直接运行,不再进入等待队列排队;如果不能直接获取,就再进入阻塞队列。非公平可能减少线程阻塞唤醒的次数,性能要比公平好一些)。

在AQS里的acquireShared方法会先调用tryAcquireShared方法再根据结果决定是执行还是等待,这里tryAcquireShared的实现就可以让Semaphore有非公平的功能。

 1     static final class NonfairSync extends Sync {
 2         private static final long serialVersionUID = -2694183684443567898L;
 3 
 4         NonfairSync(int permits) {
 5             super(permits);
 6         }
 7 
 8         protected int tryAcquireShared(int acquires) {
 9             return nonfairTryAcquireShared(acquires);
10         }
11     }

内部类FairSync:

上面说了非公平的版本,公平的版本和非公平的版本区别就在tryAcquireShared方法里面。在FairSync里,如果队列里有其它线程等待,就直接返回-1,新线程会直接放入队列中;如果没有其它线程等待才尝试获取资源。

 1     static final class FairSync extends Sync {
 2         private static final long serialVersionUID = 2014338818796000944L;
 3 
 4         FairSync(int permits) {
 5             super(permits);
 6         }
 7 
 8         protected int tryAcquireShared(int acquires) {
 9             for (;;) {
10                 if (hasQueuedPredecessors())
11                     return -1;
12                 int available = getState();
13                 int remaining = available - acquires;
14                 if (remaining < 0 ||
15                     compareAndSetState(available, remaining))
16                     return remaining;
17             }
18         }
19     }

Semaphore的构造函数:

Semaphore的构造函数有两个,都需要传许可(资源)数,另一个可以传是否公平,来决定资源分配的方式。

1     public Semaphore(int permits) {
2         sync = new NonfairSync(permits);
3     }
4 
5     public Semaphore(int permits, boolean fair) {
6         sync = fair ? new FairSync(permits) : new NonfairSync(permits);
7     }

Semaphore的其它方法基本上都是调用Sync(AQS)的方法实现的,可以看AQS的解析或Semaphore抽象内部类Sync的解析。

CountDownLatch类:

CountDownLatch类中文翻译为闭锁,它的工作机制是设定一个初值,在初值减为0之前调用await方法会使当前线程进入等待状态,调用countDown减到0时会将其它等待的线程唤醒。和Semaphore类似,它的功能也主要由继承AQS构建Sync实现。

内部类Sync:

 1     private static final class Sync extends AbstractQueuedSynchronizer {
 2         private static final long serialVersionUID = 4982264981922014374L;
 3 
 4         Sync(int count) {
 5             setState(count);
 6         }
 7 
 8         int getCount() {
 9             return getState();
10         }
11 
12         protected int tryAcquireShared(int acquires) {//如果state减到1,返回都为正;如果没有减到1,返回都为负
13             return (getState() == 0) ? 1 : -1;
14         }
15 
16         protected boolean tryReleaseShared(int releases) {
17             // Decrement count; signal when transition to zero
18             for (;;) {
19                 int c = getState();
20                 if (c == 0)
21                     return false;
22                 int nextc = c-1;//nextc是c-1
23                 if (compareAndSetState(c, nextc))//每次调用都将state减去一
24                     return nextc == 0;
25             }
26         }
27     }

await方法:

sync调用acquireSharedInterruptibly方法会调用上面实现的tryAcquireShared,如果state没减到0就返回负,线程等待进入队列。

1     public void await() throws InterruptedException {
2         sync.acquireSharedInterruptibly(1);
3     }

countDown方法:

sync调用releaseShared方法会调用上面实现的tryReleaseShared,然后会调用AQS的doReleaseShared唤醒队首的线程,队首的线程获取资源时会唤醒后面线程(因为此时调用tryAcquireShared返回值一直为正),后面线程唤醒再后面线程,直至全部唤醒。

1     public void countDown() {
2         sync.releaseShared(1);
3     }

可重入锁的实现要复杂一点,另开一篇。

posted @ 2018-06-19 19:51  Gouden  阅读(414)  评论(0编辑  收藏  举报