1.构造方法
//创建一个 Semaphore与给定数量的许可证和nonfair公平设置。
Semaphore(int permits)
//创建一个允许给定数量 Semaphore和给定的公平环境。
Semaphore(int permits, boolean fair)
参数介绍:
- permits:表示当前的凭证数量
- fair:表示当前是不是平等的(FIFO)先进先出,设置为false是表示可以插队的,比如现在三个Thread在等待permits,但是后到的竟然比我先拿到permits,这就是插队。
2.核心方法
使用semaphore可以给线程颁发一个凭证,只有获得凭证的线程才能运行,如果没有获得那就阻塞当前线程,等待其他人释放凭证才行。
其实在semaphore内部只是维护了一个count,而并不是什么凭证。我们在设置count大小时就是指定了凭证的多少。
public void acquire();//获取凭证
public void release();//释放凭证
public void acquire(int permits);//获取凭证
public void release(int permits);//获取凭证
public boolean tryAcquire();//尝试获取1个凭证
public boolean tryAcquire(int permits);//尝试获取多个凭证
public boolean tryAcquire(int permits, long timeout, TimeUnit unit);//在特定时间内获取指定个数的凭证
在尝试获取凭证时一定注意判断是不是真正的获得到了凭证,如果没有获得就不要去释放,否则会导致permits总数量改变
3.代码测试
acquire(),和release()简单的代码测试:如下有10个线程,每个线程需要1个凭证,所以同时只能运行5个线程,后面的线程只能等待别人释放才能运行。
public class MyDemo {
private static final Semaphore SEMAPHORE = new Semaphore(5);
public static void main(String[] args){
IntStream.rangeClosed(0,9).forEach(i -> {
new Thread(()->{
//拿到一个凭证
try {
System.out.println(Thread.currentThread().getName() + "start running ");
SEMAPHORE.acquire();
TimeUnit.SECONDS.sleep(2);
System.out.println(Thread.currentThread().getName() + "finished ");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
SEMAPHORE.release();
}
}).start();
});
}
}
4.使用Semaphore实现简单的锁
public static void main(String[] args) {
MyLock myLock = new MyLock();
IntStream.rangeClosed(0,3).forEach(i -> {
new Thread(()->{
System.out.println(Thread.currentThread().getName() + "执行开始!!!!");
try {
myLock.lock();
System.out.println(Thread.currentThread().getName() + "拿到了锁");
TimeUnit.SECONDS.sleep(5L);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
System.out.println(Thread.currentThread().getName() + "释放了锁");
myLock.unlock();
}
},"Thread - "+(i+1)).start();
});
}
static class MyLock{
private final Semaphore semaphore = new Semaphore(1);
public void lock() throws InterruptedException{
semaphore.acquire();
}
public void unlock(){
semaphore.release();
}
}
5.使用场景
假设有多线程去执行同一任务,但是某一时刻我只想让固定大小的线程去执行,就可以使用semaphore,来限制并发的线程数。
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(2);
new Thread(()->{
try {
semaphore.acquire(3);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release(3);
}
}).start();
}
6.注意事项
1.不论当前线程有没有获得到凭证,只要调用了acquire就会导致permits增加,所以在使用时要细心注意
如下代码所示,只获取两个凭证,但是释放了5个,所以最后结果凭证变成了6个
public class MyDemo {
private static final Semaphore SEMAPHORE = new Semaphore(3);
public static void main(String[] args) throws InterruptedException {
System.out.println(SEMAPHORE.availablePermits());
SEMAPHORE.acquire(2);
SEMAPHORE.release(5);
System.out.println(SEMAPHORE.availablePermits());
}
}