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多线程系列:一 并发工具类的使用_2 ( CountDownLatch CyclicBarrier Semaphore Exchanger )