Java中的并发工具类:Semaphore基本理解和底层实现

Posted 享叔

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java中的并发工具类:Semaphore基本理解和底层实现相关的知识,希望对你有一定的参考价值。

一.概念理解

有关Semaphore(信号量),你会看到有关材料是这样解释的:信号量是用来控制同时访问特定资源的线程数量,它通过协调各个线程,保证合理的使用公共资源。线程可以通过acquire() 方法来获取信号量的许可,当信号量中没有可用的许可的时候,线程阻塞,直到有可用的许可为止。线程可以通过release()方法释放它持有的信号量的许可。

这里面我举个例子:比如你想去一家餐馆吃饭,这家餐馆总共同时供应100人就餐,如果餐馆已经满员了,你想进去吃饭就需要排号等待,直到有客人吃完离开。你才可以进入。这里所说的餐馆我们就可以看作是线程池,餐馆里的服务员好比共享资源,每位客人都好比一个线程。Semaphore(信号量)就记录着里面的人数,要根据Semaphore(信号量)的数量来决定是否可以进入新的客人。

二.方法列表

1.构造方法:

// 创建具有给定的许可数和非公平的公平设置的 Semaphore。
Semaphore(int permits)
// 创建具有给定的许可数和给定的公平设置的 Semaphore。
Semaphore(int permits, boolean fair)

2.方法:

// 从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
void acquire()
// 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。
void acquire(int permits)
// 从此信号量中获取许可,在有可用的许可前将其阻塞。
void acquireUninterruptibly()
// 从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。
void acquireUninterruptibly(int permits)
// 返回此信号量中当前可用的许可数。
int availablePermits()
// 获取并返回立即可用的所有许可。
int drainPermits()
// 返回一个 collection,包含可能等待获取的线程。
protected Collection<Thread> getQueuedThreads()
// 返回正在等待获取的线程的估计数目。
int getQueueLength()
// 查询是否有线程正在等待获取。
boolean hasQueuedThreads()
// 如果此信号量的公平设置为 true,则返回 true。
boolean isFair()
// 根据指定的缩减量减小可用许可的数目。
protected void reducePermits(int reduction)
// 释放一个许可,将其返回给信号量。
void release()
// 释放给定数目的许可,将其返回到信号量。
void release(int permits)
// 返回标识此信号量的字符串,以及信号量的状态。
String toString()
// 仅在调用时此信号量存在一个可用许可,才从信号量获取许可。
boolean tryAcquire()
// 仅在调用时此信号量中有给定数目的许可时,才从此信号量中获取这些许可。
boolean tryAcquire(int permits)
// 如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被中断,则从此信号量获取给定数目的许可。
boolean tryAcquire(int permits, long timeout, TimeUnit unit)
// 如果在给定的等待时间内,此信号量有可用的许可并且当前线程未被中断,则从此信号量获取一个许可。
boolean tryAcquire(long timeout, TimeUnit unit)

三.源码

Semaphore是通过共享锁实现的。根据共享锁的获取原则,Semaphore分为"公平信号量"和"非公平信号量"。"公平信号量"和"非公平信号量"的释放信号量的机制是一样的,不同的是它们获取信号量的机制,线程在尝试获取信号量许可时,对于公平信号量而言,如果当前线程不在CLH队列的头部,则排队等候;而对于非公平信号量而言,无论当前线程是不是在CLH队列的头部,它都会直接获取信号量。该差异具体的体现在它们的tryAcquireShared()函数的实现不同。实现如下:

1."非公平信号量"类

static final class NonfairSync extends Sync 
    private static final long serialVersionUID = -2694183684443567898L;

    NonfairSync(int permits) 
        super(permits);
    

    protected int tryAcquireShared(int acquires) 
        return nonfairTryAcquireShared(acquires);
    

2."公平信号量"类

static final class FairSync extends Sync 
    private static final long serialVersionUID = 2014338818796000944L;

    FairSync(int permits) 
        super(permits);
    

    protected int tryAcquireShared(int acquires) 
        for (;;) 
            if (hasQueuedPredecessors())
                return -1;
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                compareAndSetState(available, remaining))
                return remaining;
        
    

四.实例演示

public class SemaphoreDemo 
    private static final int THREAD_COUNT = 20;
    private static ExecutorService executorService = Executors.newFixedThreadPool(THREAD_COUNT);
    // 创建5个许可,允许5并发执行
    private static Semaphore s = new Semaphore(5);
    public static void main(String[] args) 
        // 创建20个线程执行任务
        for (int i = 0; i < THREAD_COUNT; i++) 
            executorService.execute(new Runnable() 
                @Override
                public void run() 
                    try 
                        // 同时只能有5个线程并发执行保存数据的任务
                        s.acquire();
                        System.out.println("线程-》 " + Thread.currentThread().getName() + " ---》保存数据");
                        Thread.sleep(1000);
                        // 5个线程保存完数据,释放1个许可,其他的线程才能获取许可,继续执行保存数据的任务
                        s.release();
                        System.out.println("线程-》 " + Thread.currentThread().getName() + " ---》释放许可");
                     catch (InterruptedException e) 
                        e.printStackTrace();
                    
                
            );
        
        // 关闭线程池
        executorService.shutdown();
    

运行结果:

线程-》 pool-1-thread-1 ---》保存数据
线程-》 pool-1-thread-5 ---》保存数据
线程-》 pool-1-thread-2 ---》保存数据
线程-》 pool-1-thread-3 ---》保存数据
线程-》 pool-1-thread-4 ---》保存数据
线程-》 pool-1-thread-3 ---》释放许可
线程-》 pool-1-thread-7 ---》保存数据
线程-》 pool-1-thread-6 ---》保存数据
线程-》 pool-1-thread-5 ---》释放许可
线程-》 pool-1-thread-1 ---》释放许可
线程-》 pool-1-thread-4 ---》释放许可
线程-》 pool-1-thread-2 ---》释放许可
线程-》 pool-1-thread-10 ---》保存数据
线程-》 pool-1-thread-8 ---》保存数据
线程-》 pool-1-thread-9 ---》保存数据
线程-》 pool-1-thread-6 ---》释放许可
线程-》 pool-1-thread-12 ---》保存数据
线程-》 pool-1-thread-11 ---》保存数据
线程-》 pool-1-thread-7 ---》释放许可
线程-》 pool-1-thread-10 ---》释放许可
线程-》 pool-1-thread-13 ---》保存数据
线程-》 pool-1-thread-8 ---》释放许可
线程-》 pool-1-thread-15 ---》保存数据
线程-》 pool-1-thread-14 ---》保存数据
线程-》 pool-1-thread-9 ---》释放许可
线程-》 pool-1-thread-12 ---》释放许可
线程-》 pool-1-thread-16 ---》保存数据
线程-》 pool-1-thread-11 ---》释放许可
线程-》 pool-1-thread-17 ---》保存数据
线程-》 pool-1-thread-13 ---》释放许可
线程-》 pool-1-thread-18 ---》保存数据
线程-》 pool-1-thread-15 ---》释放许可
线程-》 pool-1-thread-19 ---》保存数据
线程-》 pool-1-thread-14 ---》释放许可
线程-》 pool-1-thread-20 ---》保存数据
线程-》 pool-1-thread-16 ---》释放许可
线程-》 pool-1-thread-17 ---》释放许可
线程-》 pool-1-thread-18 ---》释放许可
线程-》 pool-1-thread-19 ---》释放许可
线程-》 pool-1-thread-20 ---》释放许可

以上是关于Java中的并发工具类:Semaphore基本理解和底层实现的主要内容,如果未能解决你的问题,请参考以下文章

Java并发工具类之并发数控制神器Semaphore

java多线程系列:一 并发工具类的使用_2 ( CountDownLatch CyclicBarrier Semaphore Exchanger )

java高并发系列 - 第15天:JUC中的Semaphore,最简单的限流工具类,必备技能

Java并发工具类控制并发线程数的Semaphore

Java并发工具类:Semaphore

Java并发工具类Semaphore