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