Semaphore: 信号量
Semaphore: 可以指定多个线程同时访问某一资源。
一)、构造方法
//int permits:线程的准入数,即一个资源同时可以允许多少个线程访问 Semaphore semaphore = new Semaphore(int permits);
//boolean fair,指明锁的规则, false: 非公平锁, true: 公平锁 Semaphore semaphore = new Semaphore(int permits, boolean fair)
公平锁: 锁的顺序与线程的执行顺序有关
非公平锁:锁的执行顺序与线程的执行顺序无关
默认使用非公平锁。
二)、获取许可的方法
1)、acquire():获取一个许可,如果获取失败,则线程等待,等待期间可以响应中 断。
public void acquire() throws InterruptedException { //1.获取共享可中断的 sync.acquireSharedInterruptibly(1); } public final void acquireSharedInterruptibly(int arg) throws InterruptedException { //2.优先判断线程的中断状态 if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) //3.获取许可处理 doAcquireSharedInterruptibly(arg); }
2)、acquireUninterruptibly(): 获取许可,如果获取失败,则线程等待,在等待期 间不响应中断。
public void acquireUninterruptibly() { //1.获取一次许可 sync.acquireShared(1); } public final void acquireShared(int arg) { if (tryAcquireShared(arg) < 0) //2.获取许可处理 doAcquireShared(arg); }
3)、tryAcquire(): 尝试获取许可,若成功立即返回true,失败立即返回false。
protected boolean tryAcquire(int arg) { throw new UnsupportedOperationException(); }
4)、tryAcquire(long time, TimeUnit unit):
在指定时间内尝试获取许可,若获取失败返回false,成功返回true,在指定的时间内可以响应中断。
public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { //1.获取一次许可 return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException { //2.判断线程的中断状态 if (Thread.interrupted()) throw new InterruptedException(); return tryAcquireShared(arg) >= 0 || //3.做获取许可的操作 doAcquireSharedNanos(arg, nanosTimeout); }
三)、释放许可:
relase():开放一个线程访问资源,相当于关闭锁。
public void release() { //释放一个共享 sync.releaseShared(1); }
四)、信号量的使用举例
模拟食堂吃饭情景:
食堂相当于一个共享资源,去食堂吃饭的人相当于一个访问食堂的线程,多个
人同时进食堂吃饭,但食堂一次只能容纳5个人,如何控制食堂这个共享资源,
语序5个线程同时访问。
使用Semaphore。
食堂访问线程:Canteen_SemahoreThread
一次只能允许5个线程访问
public class Canteen_SemaphoreThread implements Runnable{ /** * 控制食堂的人流量,一次只允许5个人同时吃饭 */ private Semaphore semaphore ; public Canteen_SemaphoreThread(Semaphore semaphore) { this.semaphore = semaphore; } @Override public void run() { //获取准入许可,相当于获取锁,若无法获取许可,线程则一直等待释放许可,等待过程中优先响应中断 try { /** * 使用信号量来锁住一个资源,传入信号量时,指明允许资源同时访问的线程个数。 */ semaphore.acquire(); System.out.println("我正在吃饭。。。"); Thread.sleep(100); System.out.println("我吃完了、、、"); } catch (InterruptedException e) { e.printStackTrace(); }finally { //释放一个许可 semaphore.release(); } } }
消费食堂线程: Canteen_SemaphoreTest
开启20个线程,一次只能有5个人在食堂吃饭。
/** * 测试信号量的使用,开启20个线程 */ public class Canteen_SemaphoreTest { public static void main(String[] args) { //设置信号量的大小,一次食堂只能容纳5个人吃饭 Semaphore semaphore = new Semaphore(5); ExecutorService executor = Executors.newFixedThreadPool(20); Canteen_SemaphoreThread canteenThread = new Canteen_SemaphoreThread(semaphore); for(int i = 0; i < 20; i++){ executor.execute(canteenThread); } } }
结果:
我正在吃饭。。。 pool-1-thread-1 我正在吃饭。。。 pool-1-thread-2 我正在吃饭。。。 pool-1-thread-3 我正在吃饭。。。 pool-1-thread-4 我正在吃饭。。。 pool-1-thread-5 我吃完了、、、 pool-1-thread-1 我正在吃饭。。。 pool-1-thread-6 我吃完了、、、 pool-1-thread-4 我吃完了、、、 pool-1-thread-5 我正在吃饭。。。 pool-1-thread-7 我正在吃饭。。。 pool-1-thread-8 我吃完了、、、 pool-1-thread-2 我吃完了、、、 pool-1-thread-3 我正在吃饭。。。 pool-1-thread-9 我正在吃饭。。。 pool-1-thread-10 我吃完了、、、 pool-1-thread-6 我正在吃饭。。。 pool-1-thread-11 我吃完了、、、 pool-1-thread-8 我吃完了、、、 pool-1-thread-7 我吃完了、、、 pool-1-thread-9 我吃完了、、、 pool-1-thread-10 我正在吃饭。。。 pool-1-thread-14 我正在吃饭。。。 pool-1-thread-13 我正在吃饭。。。 pool-1-thread-12 我正在吃饭。。。 pool-1-thread-15 我吃完了、、、 pool-1-thread-11 我正在吃饭。。。 pool-1-thread-16 我吃完了、、、 pool-1-thread-12 我吃完了、、、 pool-1-thread-15 我正在吃饭。。。 pool-1-thread-17 我正在吃饭。。。 pool-1-thread-18 我吃完了、、、 pool-1-thread-13 我正在吃饭。。。 pool-1-thread-19 我吃完了、、、 pool-1-thread-14 我正在吃饭。。。 pool-1-thread-20 我吃完了、、、 pool-1-thread-16 我吃完了、、、 pool-1-thread-19 我吃完了、、、 pool-1-thread-18 我吃完了、、、 pool-1-thread-20 我吃完了、、、 pool-1-thread-17
结果分析:
使用Semaphore semaphore = new Semaphore(5),指明共享资源允许同时访问的线程个数,开启20个食堂线程,有5个线程可以同时访问食堂,因此,刚开始打印了5个我正在吃饭,线程无等待。
代码gitHub地址:https://github.com/slob-cow/java_performance_optimization/tree/master/semaphore