• 【JUC源码解析】Semaphore


    简介

    Semaphore(信号量),概念上讲,一个信号量持有一组许可(permits)。

    概述

    线程可调用它的acquire()方法获取一个许可,不成功则阻塞;调用release()方法来归还一个许可,前提是已经拿到过一个许可。然而,并没有什么实际的许可对象,Semaphore只是记录了一个数字,并根据这个数字管控线程。

    应用

    描述

    有一组数据items,其实是26个大写的英文字母,有52个线程从中取数据(getItem),每个线程取一个数据,有一个线程不停地往里面放数据(putItem)直至取数据的52个线程都取到了数据。有一个信号量(Semaphore),要求同时只能有10个线程从中取数据(取数据之前会拿一个许可,共有10许可),除非,某个线程取得的数据,又被放数据的线程放进去了,模拟取数据线程操作结束,把许可证归还,其他取数据线程才能从中拿到许可,然后取数据。

    代码

      1 public class Pool {
      2     private static final int MAX_AVAILABLE = 10;
      3     private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
      4 
      5     public String getItem() throws InterruptedException {
      6         available.acquire();
      7         return getNextAvailableItem();
      8     }
      9 
     10     public void putItem(String x) {
     11         if (markAsUnused(x))
     12             available.release();
     13     }
     14 
     15     private static void sleep(int sleep) {
     16         try {
     17             Thread.sleep(sleep);
     18         } catch (InterruptedException e) {
     19             Thread.currentThread().interrupt();
     20         }
     21     }
     22 
     23     protected boolean[] used = new boolean[MAX_AVAILABLE];
     24 
     25     protected synchronized String getNextAvailableItem() {
     26         for (int i = 0; i < MAX_AVAILABLE; ++i) {
     27             if (!used[i]) {
     28                 used[i] = true;
     29                 return items[i];
     30             }
     31         }
     32         return null;
     33     }
     34 
     35     int len = 26;
     36     protected String[] items = new String[len];
     37     {
     38         initial(0);
     39     }
     40 
     41     void initial(int sleep) {
     42         for (int i = 0; i < len; i++) {
     43             sleep(sleep);
     44             items[i] = String.valueOf((char) (i + 65));
     45             putItem(items[i]);
     46         }
     47     }
     48 
     49     protected synchronized boolean markAsUnused(String item) {
     50         for (int i = 0; i < MAX_AVAILABLE; ++i) {
     51             if (item == items[i]) {
     52                 if (used[i]) {
     53                     used[i] = false;
     54                     return true;
     55                 } else
     56                     return false;
     57             }
     58         }
     59         return false;
     60     }
     61 
     62     public static void main(String[] args) {
     63         final Pool pool = new Pool();
     64 
     65         final AtomicBoolean hasDone = new AtomicBoolean(false);
     66 
     67         ExecutorService es = Executors.newFixedThreadPool(52);
     68 
     69         new Thread(new Runnable() { // 计时器,每10毫秒记录一次
     70             @Override
     71             public void run() {
     72                 while (!hasDone.get()) {
     73                     System.out.print(".");
     74                     sleep(10);
     75                 }
     76 
     77             }
     78         }).start();
     79         new Thread(new Runnable() { // 1秒后,每20毫秒添加一个元素
     80             @Override
     81             public void run() {
     82                 sleep(1000);
     83                 while (!hasDone.get()) {
     84                     pool.initial(20);
     85                 }
     86             }
     87         }).start();
     88 
     89         final CyclicBarrier barrier = new CyclicBarrier(52, new Runnable() {
     90             @Override
     91             public void run() {
     92                 hasDone.set(true);
     93             }
     94         });
     95         for (int i = 0; i < 52; i++) {
     96             es.execute(new Runnable() {
     97                 @Override
     98                 public void run() {
     99                     try {
    100                         System.out.println(Thread.currentThread() + " << " + pool.getItem());
    101                         barrier.await();
    102                     } catch (InterruptedException e) {
    103                         Thread.currentThread().interrupt();
    104                     } catch (BrokenBarrierException e) {
    105                         e.printStackTrace();
    106                     }
    107                 }
    108             });
    109         }
    110         es.shutdown();
    111     }
    112 }

    输出

    .Thread[pool-1-thread-1,5,main] << A
    Thread[pool-1-thread-2,5,main] << B
    Thread[pool-1-thread-3,5,main] << C
    Thread[pool-1-thread-4,5,main] << D
    Thread[pool-1-thread-5,5,main] << E
    Thread[pool-1-thread-6,5,main] << F
    Thread[pool-1-thread-7,5,main] << G
    Thread[pool-1-thread-8,5,main] << H
    Thread[pool-1-thread-10,5,main] << I
    Thread[pool-1-thread-9,5,main] << J
    ...............................................................................................Thread[pool-1-thread-12,5,main] << A
    ..Thread[pool-1-thread-11,5,main] << B
    ..Thread[pool-1-thread-13,5,main] << C
    ..Thread[pool-1-thread-14,5,main] << D
    ..Thread[pool-1-thread-15,5,main] << E
    ..Thread[pool-1-thread-16,5,main] << F
    ..Thread[pool-1-thread-17,5,main] << G
    ..Thread[pool-1-thread-18,5,main] << H
    ..Thread[pool-1-thread-19,5,main] << I
    .Thread[pool-1-thread-20,5,main] << J
    ...............................Thread[pool-1-thread-21,5,main] << A
    ..Thread[pool-1-thread-22,5,main] << B
    ..Thread[pool-1-thread-24,5,main] << C
    ..Thread[pool-1-thread-23,5,main] << D
    ..Thread[pool-1-thread-25,5,main] << E
    ..Thread[pool-1-thread-26,5,main] << F
    ..Thread[pool-1-thread-27,5,main] << G
    ..Thread[pool-1-thread-28,5,main] << H
    ..Thread[pool-1-thread-29,5,main] << I
    ..Thread[pool-1-thread-30,5,main] << J
    .................................Thread[pool-1-thread-31,5,main] << A
    ...Thread[pool-1-thread-32,5,main] << B
    .Thread[pool-1-thread-33,5,main] << C
    ..Thread[pool-1-thread-34,5,main] << D
    ..Thread[pool-1-thread-35,5,main] << E
    ..Thread[pool-1-thread-36,5,main] << F
    ..Thread[pool-1-thread-37,5,main] << G
    ..Thread[pool-1-thread-38,5,main] << H
    ..Thread[pool-1-thread-39,5,main] << I
    ..Thread[pool-1-thread-42,5,main] << J
    .................................Thread[pool-1-thread-41,5,main] << A
    ..Thread[pool-1-thread-40,5,main] << B
    ..Thread[pool-1-thread-43,5,main] << C
    ..Thread[pool-1-thread-44,5,main] << D
    ..Thread[pool-1-thread-52,5,main] << E
    ..Thread[pool-1-thread-45,5,main] << F
    ..Thread[pool-1-thread-51,5,main] << G
    ..Thread[pool-1-thread-50,5,main] << H
    ..Thread[pool-1-thread-49,5,main] << I
    ..Thread[pool-1-thread-48,5,main] << J
    .................................Thread[pool-1-thread-47,5,main] << A
    ..Thread[pool-1-thread-46,5,main] << B

    从上面输出可知,每10条记录,都要等一段时间,恰好体现了信号量管控的现象

    源码分析

    同步器对象

    1     private final Sync sync; // 同步器对象

    内部类

    Sync

     1     abstract static class Sync extends AbstractQueuedSynchronizer { // 内部类,继承自AQS
     2         private static final long serialVersionUID = 1192457210091910933L;
     3 
     4         Sync(int permits) { // 构造方法
     5             setState(permits); // 初始化许可的个数,对应state
     6         }
     7 
     8         final int getPermits() { // 获取当前许可的个数
     9             return getState();
    10         }
    11 
    12         final int nonfairTryAcquireShared(int acquires) { // 获取锁,默认非公平锁
    13             for (;;) {
    14                 int available = getState(); // 获取当前state,可用permits个数
    15                 int remaining = available - acquires; // 可用 - 获取 = 剩余
    16                 if (remaining < 0 || compareAndSetState(available, remaining)) // 如果remaining小于0,直接返回;否则CAS
    17                                                                                 // state为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) // 溢出,说明releases的值小于0
    27                     throw new Error("Maximum permit count exceeded");
    28                 if (compareAndSetState(current, next)) // CAS state
    29                                                         // 为next,成功则返回;否则,重新获取并释放
    30                     return true;
    31             }
    32         }
    33 
    34         final void reducePermits(int reductions) { // 减少许可个数
    35             for (;;) {
    36                 int current = getState(); // 当前可用的个数
    37                 int next = current - reductions; // 剩余
    38                 if (next > current) // 说明reductions小于0
    39                     throw new Error("Permit count underflow");
    40                 if (compareAndSetState(current, next)) // CAS state为next,成功则返回;否则,重新获取并减少
    42                     return;
    43             }
    44         }
    45 
    46         final int drainPermits() { // 排空许可,即情况所有的许可
    47             for (;;) {
    48                 int current = getState(); // 获取当前状态
    49                 if (current == 0 || compareAndSetState(current, 0)) // CAS state
    50                                                                     // 为0
    51                     return current;
    52             }
    53         }
    54     }

    NonfairSync

     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

     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 || compareAndSetState(available, remaining))
    15                     return remaining;
    16             }
    17         }
    18     }

    构造方法

    1     public Semaphore(int permits) {
    2         sync = new NonfairSync(permits); // 默认非公平
    3     }
    4 
    5     public Semaphore(int permits, boolean fair) { // 根据fair参数选择公平或非公平
    6         sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    7     }

    其它方法

     1     public void acquire() throws InterruptedException {
     2         sync.acquireSharedInterruptibly(1); // 获取许可,响应中断
     3     }
     4 
     5     public void acquireUninterruptibly() {
     6         sync.acquireShared(1); // 不响应中断
     7     }
     8 
     9     public boolean tryAcquire() {
    10         return sync.nonfairTryAcquireShared(1) >= 0; // false: < 0   true: > 0
    11     }
    12 
    13     public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {
    14         return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); // 支持超时
    15     }
    16 
    17     public void release() {
    18         sync.releaseShared(1); // 释放许可
    19     }
    20 
    21     public void acquire(int permits) throws InterruptedException { // 获取多个
    22         if (permits < 0)
    23             throw new IllegalArgumentException();
    24         sync.acquireSharedInterruptibly(permits); // 响应中断
    25     }
    26 
    27     public void acquireUninterruptibly(int permits) { // 不响应中断
    28         if (permits < 0)
    29             throw new IllegalArgumentException();
    30         sync.acquireShared(permits);
    31     }
    32 
    33     public boolean tryAcquire(int permits) { //  false: < 0   true: > 0
    34         if (permits < 0)
    35             throw new IllegalArgumentException();
    36         return sync.nonfairTryAcquireShared(permits) >= 0;
    37     }
    38 
    39     public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException {
    40         if (permits < 0)
    41             throw new IllegalArgumentException();
    42         return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout)); // 支持超时
    43     }
    44 
    45     public void release(int permits) { // 释放多个
    46         if (permits < 0)
    47             throw new IllegalArgumentException();
    48         sync.releaseShared(permits);
    49     }
    50 
    51     public int availablePermits() { // 可用的许可个数
    52         return sync.getPermits();
    53     }
    54 
    55     public int drainPermits() { // 排空
    56         return sync.drainPermits();
    57     }
    58 
    59     protected void reducePermits(int reduction) { // 减少
    60         if (reduction < 0)
    61             throw new IllegalArgumentException();
    62         sync.reducePermits(reduction);
    63     }
    64 
    65     public boolean isFair() { // 是否是公平的
    66         return sync instanceof FairSync;
    67     }
    68 
    69     public final boolean hasQueuedThreads() {
    70         return sync.hasQueuedThreads(); // 队列里是否还有等待的线程
    71     }
    72 
    73     public final int getQueueLength() {
    74         return sync.getQueueLength(); // 队列长度,等待线程的个数
    75     }
    76 
    77     protected Collection<Thread> getQueuedThreads() {
    78         return sync.getQueuedThreads(); // 获取在对列里等待的线程
    79     }
    80 
    81     public String toString() { // toString()方法
    82         return super.toString() + "[Permits = " + sync.getPermits() + "]";
    83     }

     源码比较简单。

    行文至此结束。

    尊重他人的劳动,转载请注明出处:http://www.cnblogs.com/aniao/p/aniao_semaphore.html

  • 相关阅读:
    简化日常工作之三:自己写一个CI脚手架
    gearman的安装和配置
    简化日常工作系列之二 ----- 定时采集小说
    简化日常工作系列之一 ---- 自动新建每日记录
    代码简洁之四 统一抽象层次
    php处理金额显示的一些笔记
    代码简洁之三:减少注释 增加代码可读性
    通用性安装redis和基本配置
    写一个Redis封装类
    Exchange2010安装指南
  • 原文地址:https://www.cnblogs.com/aniao/p/aniao_semaphore.html
Copyright © 2020-2023  润新知