Java 中的 Semaphore 信号量使用方法

Semaphore 可以很轻松完成信号量控制,Semaphore可以控制某个资源可被同时访问的个数,通过 acquire() 获取一个许可,如果没有就等待,而 release() 释放一个许可。

Semaphore

Semaphore 有两个构造函数,参数为许可的个数 permits 和是否公平竞争 fair。通过 acquire 方法能够获得的许可个数为 permits,如果超过了这个个数,就需要等待。当一个线程 release 释放了一个许可后,fair 决定了正在等待的线程该由谁获取许可,如果是公平竞争则等待时间最长的线程获取,如果是非公平竞争则随机选择一个线程获取许可。不传 fair 的构造函数默认采用非公开竞争。

Semaphore(int permits)
Semaphore(int permits, boolean fair)

一个线程可以一次获取一个许可,也可以一次获取多个。 在 acquire 等待的过程中,如果线程被中断,acquire 会抛出中断异常,如果希望忽略中断继续等待可以调用 acquireUninterruptibly 方法。同时提供了 tryAcquire 方法尝试获取,获取失败返回 false,获取成功返回 true。tryAcquire 方法可以在获取不到时立即返回,也可以等待一段时间。需要注意的是,没有参数的 tryAcquire 方法在有许可可以获取的情况下,无论有没有线程在等待都能立即获取许可,即便是公平竞争也能立即获取。

public void acquire()
public void acquireUninterruptibly()
public boolean tryAcquire()
public boolean tryAcquire(long timeout, TimeUnit unit)
public void release()

public void acquire(int permits)
public void acquireUninterruptibly(int permits)
public boolean tryAcquire(int permits)
public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
public void release(int permits)

使用示例

如下的示例中,测试方法 test 创建了多个线程,每个线程启动后都调用 acquire 方法,然后延时 5s 模仿业务耗时,最后调用 release 方法释放许可。

public class SemaphoreTest {
    private int threadNum;
    private Semaphore semaphore;

    public SemaphoreTest(int permits,int threadNum, boolean fair) {
        this.threadNum = threadNum;
        semaphore = new Semaphore(permits,fair);
    }
    
    private void println(String msg){
        SimpleDateFormat sdf = new SimpleDateFormat("[YYYY-MM-dd HH:mm:ss.SSS] ");
        System.out.println(sdf.format(new Date()) + msg);
    }
    
    public void test(){
        for(int i =  0; i < threadNum; i ++){
            new Thread(() -> {
                try {
                    semaphore.acquire();
                    println(Thread.currentThread().getName() + " acquire");
                    Thread.sleep(5000);//模拟业务耗时
                    println(Thread.currentThread().getName() + " release");
                    semaphore.release();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

公平与非公平

在上述的示例中,如果 fair 传的是 true,则各个线程公平竞争,即按照等待时间的长短决定谁先获取许可。以 9 个线程竞争 3 个许可为例,执行结果如下,首选是线程 0、1、2 获取了许可,5s 后线程 3、4、5 获取了许可,最后是线程 6、7、8 获取许可,顺序基本上与创建线程并启动的先后顺序一致,也与各个线程等待的时间基本相符。

[2017-08-20 21:47:21.817] Thread-0 acquire
[2017-08-20 21:47:21.817] Thread-2 acquire
[2017-08-20 21:47:21.817] Thread-1 acquire
[2017-08-20 21:47:26.830] Thread-1 release
[2017-08-20 21:47:26.830] Thread-0 release
[2017-08-20 21:47:26.830] Thread-4 acquire
[2017-08-20 21:47:26.830] Thread-3 acquire
[2017-08-20 21:47:26.831] Thread-2 release
[2017-08-20 21:47:26.831] Thread-5 acquire
[2017-08-20 21:47:31.831] Thread-4 release
[2017-08-20 21:47:31.831] Thread-3 release
[2017-08-20 21:47:31.831] Thread-6 acquire
[2017-08-20 21:47:31.831] Thread-7 acquire
[2017-08-20 21:47:31.832] Thread-5 release
[2017-08-20 21:47:31.832] Thread-8 acquire
[2017-08-20 21:47:36.831] Thread-6 release
[2017-08-20 21:47:36.831] Thread-7 release
[2017-08-20 21:47:36.832] Thread-8 release

在上述的示例中,如果 fair 传的是 false,则各个线程非公平竞争,随机选取一个线程获取许可。以 9 个线程竞争 3 个许可为例,执行结果如下,首先是线程 0、1、3 获取了许可,5s 后线程 2、5、7 获取了许可,最后是线程 4、6、8 获取许可,与线程创建启动时间无关,也与线程等待时间无关。

[2017-08-20 17:45:09.893] Thread-0 acquire
[2017-08-20 17:45:09.893] Thread-3 acquire
[2017-08-20 17:45:09.893] Thread-1 acquire
[2017-08-20 17:45:14.895] Thread-3 release
[2017-08-20 17:45:14.895] Thread-0 release
[2017-08-20 17:45:14.895] Thread-5 acquire
[2017-08-20 17:45:14.895] Thread-1 release
[2017-08-20 17:45:14.896] Thread-7 acquire
[2017-08-20 17:45:14.896] Thread-2 acquire
[2017-08-20 17:45:19.895] Thread-5 release
[2017-08-20 17:45:19.895] Thread-4 acquire
[2017-08-20 17:45:19.896] Thread-7 release
[2017-08-20 17:45:19.896] Thread-6 acquire
[2017-08-20 17:45:19.896] Thread-2 release
[2017-08-20 17:45:19.896] Thread-8 acquire
[2017-08-20 17:45:24.895] Thread-4 release
[2017-08-20 17:45:24.896] Thread-8 release
[2017-08-20 17:45:24.896] Thread-6 release

分享学习笔记和技术总结,内容涉及 Java 进阶、虚拟机、MySQL、NoSQL、分布式计算、开源框架等多个领域,欢迎关注作者。

Java 中的 Semaphore 信号量使用方法_第1张图片
image.png

你可能感兴趣的:(Java 中的 Semaphore 信号量使用方法)