1、semaphore概述
semaphore可以看作是一个许可的集合,用于控制多个线程对有限共享资源的访问。当某个线程需要访问资源时,必须首先从semaphore获取一个许可,使用完资源后再将许可归还给semaphore。
2、semaphore常用方法
控制共享资源的多线程访问
(1)初始化许可数量
private static final int MAX_AVAILABLE = 100;
private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);(2)获得许可
available.acquire(); // 从Semaphore获取【一个】许可,成功则立即返回,并将剩余许可数减1,如果没有可用许可则阻塞直至拿到可用许可
available.acquire(int num); // 一次获取【多个】
available.acquireUninterruptibly(); // 与acquire()区别是当线程发生中断后,是否还继续等待获取许可
available.tryAcquire(); // 与acquire()区别是当前没有可用许可时,是否阻塞
available.tryAcquire(timeout, unit); // 不阻塞,在指定时间内没有可用许可则直接返回false(3)归还许可
available.release(); // 归还【一个】许可给Semaphore,并将许可数加1
available.release(int num); // 归还【多个】注意:release()释放的许可不一定是acquire()获取的许可
(4)查询可用许可数量
available.availablePermits();
当做互斥锁使用
private final Semaphore available = new Semaphore(1);
当许可只有1个时,可以实现类似synchronized或lock的功能,任何时候只能有一个线程操作共享资源
支持公平与非公平模式
等待时间长的线程是否优先获得许可
3、semaphore应用
控制客户端在单位时间内调用某个远程服务不超过N次
public class TestSemaphore2 {
final static int MAX_QPS = 10;
final static Semaphore semaphore = new Semaphore(MAX_QPS);
public static void main (String ... args) throws Exception {
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
semaphore.release(MAX_QPS-semaphore.availablePermits()); // 防止许可一直递增
System.out.println("--------------------------");
}
}, 5000, 5000, TimeUnit.MILLISECONDS);
//lots of concurrent calls:100 * 1000
ExecutorService pool = Executors.newFixedThreadPool(100);
for (int i=100;i>0;i--) {
final int x = i;
pool.submit(new Runnable() {
@Override
public void run() {
for (int j=1000;j>0;j--) {
semaphore.acquireUninterruptibly(1);
remoteCall(x, j);
}
}
});
}
pool.shutdown();
pool.awaitTermination(1, TimeUnit.HOURS);
System.out.println("DONE");
}
private static void remoteCall(int i, int j) {
System.out.println(String.format("%s - %s: %d %d",new Date(),
Thread.currentThread(), i, j));
}
}