@[TOC](高并发(10)- 线程并发工具类-Semaphore)
前言
上篇文章讲解了线程的并发工具类之CyclicBarrier,本文就来讲解Semaphore并发工具类
CyclicBarrier其实就是一个栅栏,需要等所有线程都到达栅栏才可以继续执行。
Semaphore则是类似于信号量,有一个固定数量的凭证,线程来了拿走一个,用完还回来,当没有凭证可使用的时候,就开始等待
什么是Semaphore
Semaphore就是一个信号量。必须是获取他的线程释放,通常用来现在某些资源访问数量的场景使用。
Semaphore执行图如上图所示,Semaphore定义了4个,当有线程进来时,需要通过acquire()方法取走一个凭证,当用完时,在通过release()方法将凭证返回。而当有多个线程涌进时,只有四个线程取到了凭证,其他的线程就需要等待其他线程release()返回凭证,然后执行。
就如同生活中,去饭店吃饭,只有四张桌子,所以同时只允许四波客人吃饭,如果同时来了五批客人,服务员会让四批客人就做(获取凭证),第五批客人则是等待,因为这时候没有座位了,也就是没有凭证了,需要等有桌客人离坐(释放凭证),然后招呼第五批客人入座(获取凭证)。
一个信号量有且仅有 3 种操作,且它们全部是原子的。
初始化、增加和减少。
增加可以为一个进程解除阻塞。
减少可以让一个进程进入阻塞。
Semaphore有什么用
Semaphore有两个目的,第一个目的是多个共享资源互斥使用,第二目的是并发线程数的控制
1.共享资源互斥使用
例如我给我个共享资源加上了Semaphore等于1,所以肯定只有一个线程可以来使用这个资源,这就达到了共享资源互斥使用,
2.并发线程数的控制
比如我们有个线程池,需要限定并发线程的数量,我们就可以使用Semaphore来指定并发数量,所有的线程进来时都要获取凭证,取不到则等待,这就达到了并发线程数的控制效果。
Semaphore的实现
构造方法
//创建指定长度的信号量,不公平的,
public Semaphore(int permits);
//创建指定长度的信号量,fair为true则是公平的
public Semaphore(int permits,boolean fair);
所谓的公平信号量是获得锁的顺序与调用semaphore.acquire()的顺序有关,但不代表100%地获得信号量,仅仅是在概率上能得到保证。而非公平信号量就是无关的了。
Semaphore(int permits)构造方法是创建指定数量的信号量,属于不公平的信号量。
Semaphore(int permits,boolean fair);构造方法是创建指定数量的信号量,fair为true则是公平的信号量,否则相反。
普通方法
//获取一个平成
public void acquire();
//获取指定数量的凭证,在提供这些凭证前一直将线程阻塞。比如n=2,就相当于占用了两个凭证
public void acquire(int n);
//释放一个凭证
public void release();
//释放n个凭证
public void release(int n);
//当前可用的凭证数
public int availablePermits();;
代码实现
/**
* @version 1.0
* @Description SemaphoreDemo
* @Author wb.yang
* @Date 2020/4/3 7:21
*/
public class SemaphoreDemo {
/**
* 创建指定长度信号量
*/
private static final Semaphore SEMAPHORE = new Semaphore(2);
/**
* 工作线程
*/
public static class SubThread implements Runnable {
@Override
public void run() {
long id = Thread.currentThread().getId();
try {
System.out.println("Thread_" + id + "准备开始获取凭证");
SEMAPHORE.acquire();
System.out.println("Thread_" + id + "获取到凭证执行输出,剩余凭证:" + SEMAPHORE.availablePermits());
Thread.sleep(1000);
System.out.println("Thread_" + id + "准备开始返回凭证");
SEMAPHORE.release();
System.out.println("Thread_" + id + "返回凭证输出,剩余凭证:" + SEMAPHORE.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
for (int i = 0; i <= 3; i++) {
Thread thread = new Thread(new SubThread());
thread.start();
}
}
}
从代码中看出,我们定义了两个信号量,然后我们有一个工作线程。先获取了凭证,然后在执行业务逻辑,最后返回凭证,打印凭证数量。然后启动了四个线程,来测试这个代码。
代码返回结果
从代码结果可以看出,四个线程开始准备获取凭证,然后然后两个线程获取到了凭证,并且输出了剩余的凭证。然后剩下两个线程就在等待凭证,当哪两个线程执行完,释放了凭证,剩下的线程再去去获取凭证,然后执行之后的业务。
网友评论