JDK 源码解析 —— Semaphore

零. 简介
这是一个用来对并发计数的信号量,并发量超过一定数值则只能等待。从概念上来说,semaphore 维持着一组许可证。获取锁的时候,需要先获得 semaphore 的许可才行。



一. 从 Demo 解析源码

[java]  view plain  copy
  1. package com.wenniuwuren.concurrent;  
  2.   
  3. import java.util.concurrent.ExecutorService;  
  4. import java.util.concurrent.Executors;  
  5. import java.util.concurrent.Semaphore;  
  6.   
  7. /** 
  8.  * Created by zhuyb on 16/5/1. 
  9.  */  
  10. public class SemaphoreTest {  
  11.   
  12.     public static void main(String[] args) {  
  13.         ExecutorService executorService = Executors.newCachedThreadPool();  
  14.   
  15.         Semaphore semaphore = new Semaphore(5);  
  16.   
  17.         for (int i = 0; i < 10; i++) {  
  18.             int count = i;  
  19.             Runnable runnable = new Runnable() {  
  20.                 @Override  
  21.                 public void run() {  
  22.                     try {  
  23.                         semaphore.acquire(); // 获取许可  
  24.                         System.out.println("当前循环:" + count);  
  25.                         System.out.println("当前还剩多少许可数量:" + semaphore.availablePermits());  
  26.                         Thread.sleep(10000);  
  27.                         semaphore.release(); // 释放占用的许可  
  28.                     } catch (Exception e) {  
  29.                         e.printStackTrace();  
  30.                     }  
  31.   
  32.                 }  
  33.             };  
  34.             executorService.execute(runnable);  
  35.         }  
  36.         executorService.shutdown();  
  37.     }  
  38. }  

(1) 先看 Semaphore semaphore = new Semaphore(5) 的构造函数
默认使用非公平锁,调用的是继承自 AQS 的内部类 NonfairSync 
[java]  view plain  copy
  1. public Semaphore(int permits) {  
  2.     sync = new NonfairSync(permits);  
  3. }  

看下 NonfairSync 的具体实现:构造函数调用父类来初始化,其实就是 AQS 的 Sync 构造函数

[java]  view plain  copy
  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. }  


AQS 的 Sync 构造函数:设置 AQS 的同步状态 stat

[java]  view plain  copy
  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. // 省略无用代码  
  9. }  


AQS 的状态值由具体调用的类来定义 stat 的含义,对于 Semaphore 来说 stat 的数量含义就是可以有多少个线程并发使用某个资源

[java]  view plain  copy
  1. protected final void setState(int newState) {  
  2.     state = newState;  
  3. }  

[java]  view plain  copy
  1. 2)semaphore.acquire(); // 获取许可  
[java]  view plain  copy
  1. public void acquire() throws InterruptedException {  
  2.     sync.acquireSharedInterruptibly(1);  
  3. }  
  4.   
  5. public final void acquireSharedInterruptibly(int arg)  
  6.         throws InterruptedException {  
  7.     if (Thread.interrupted())  
  8.         throw new InterruptedException();  
  9.     if (tryAcquireShared(arg) < 0)  
  10.         doAcquireSharedInterruptibly(arg);  
  11. }  


acquireSharedInterruptibly 从代码看出来是响应中断的,再看下 tryAcquireShared(arg):AQS 中 tryAcquireShared 没有具体实现,交给具体的继承类去实现

[java]  view plain  copy
  1. protected int tryAcquireShared(int arg) {  
  2.     throw new UnsupportedOperationException();  
  3. }  

下面是 Semaphore 的 tryAcquireShared 具体实现:可以看到用当前的 stat 值减去传入的 1。举个具体的例子,如果是第一次调用 semaphore.acquire(), 则 available 就等于初始化  Semaphore 时候的值,然后减去 acquires=1,如果小于零就要调用 doAcquireSharedInterruptibly(arg) 这个方法是在 AQS 的 FIFO 队列中排队;如果大于零则 CAS 更新 AQS 的 stat 值,说明线程获得了 Semaphore 的许可,可以成功执行
[java]  view plain  copy
  1. protected int tryAcquireShared(int acquires) {  
  2.     return nonfairTryAcquireShared(acquires);  
  3. }  
  4.   
  5. final int nonfairTryAcquireShared(int acquires) {  
  6.     for (;;) {  
  7.         int available = getState();  
  8.         int remaining = available - acquires;  
  9.         if (remaining < 0 ||  
  10.             compareAndSetState(available, remaining))  
  11.             return remaining;  
  12.     }  
  13. }  


(3)semaphore.release(); // 释放占用的许可

[java]  view plain  copy
  1. public void release() {  
  2.     sync.releaseShared(1);  
  3. }  
  4.   
  5. public final boolean releaseShared(int arg) {  
  6.     if (tryReleaseShared(arg)) {  
  7.         doReleaseShared();  
  8.         return true;  
  9.     }  
  10.     return false;  
  11. }  


tryReleaseShared(arg) 和 tryAcquireShared() 一样都是需要具体类自己实现的,这样才能由该类定义 stat 的具体含义:参数 releases =1 表示释放一个 Semaphore, CAS 设置新的 stat 值

[java]  view plain  copy
  1. protected final boolean tryReleaseShared(int releases) {  
  2.     for (;;) {  
  3.         int current = getState();  
  4.         int next = current + releases;  
  5.         if (next < current) // overflow  
  6.             throw new Error("Maximum permit count exceeded");  
  7.         if (compareAndSetState(current, next))  
  8.             return true;  
  9.     }  
  10. }  


[java]  view plain  copy
  1. 接下来看 tryReleaseShared 返回 true 后进入的 doReleaseShared():与互斥锁不同的是,共享锁在释放锁的时候会将共享锁释放信息向 AQS 的队列中传播这个共享锁已经释放的 SIGNAL,这样等待这个共享锁的线程就能较快地脱离 AQS 的等待队列。从 ws == Node.SIGNAL 分支执行的就是共享锁的向后传递 SIGNAL 方法 unparkSuccessor(h)。最外面的 for(;;)就是为了保证释放锁的正常进行,异常就是循环重试  
[java]  view plain  copy
  1. private void doReleaseShared() {  
  2.     for (;;) {  
  3.         Node h = head;  
  4.         if (h != null && h != tail) {  
  5.             int ws = h.waitStatus;  
  6.             if (ws == Node.SIGNAL) {  
  7.                 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))  
  8.                     continue;            // loop to recheck cases  
  9.                 unparkSuccessor(h);  
  10.             }  
  11.             else if (ws == 0 &&  
  12.                      !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))  
  13.                 continue;                // loop on failed CAS  
  14.         }  
  15.         if (h == head)                   // loop if head changed  
  16.             break;  
  17.     }  
  18. }  



二. 总结

Semaphore 是借助 AQS(AbstractQueuedSynchronizer) 这个同步控制器来实现共享锁的获取和释放,AQS 中的同步变量 stat 在 Semaphore 的意思就是并发的数量,线程并发数量超过这个 stat 总数,之后的线程只能进入 AQS 的等待队列直到其他线程释放这个 stat,然后公平地排队获取锁或者非公平地抢占锁。 



转载自:https://blog.csdn.net/wenniuwuren/article/details/51302745

猜你喜欢

转载自blog.csdn.net/jr_way/article/details/80173827