Semaphore
- 简介
- 常用方法
- 示例
简介
多用于线程多于资源下的资源控制的线程控制器。很像锁同步,和锁同步不同的是锁锁定一个资源,同时只能有一个线程操作这个资源。而Semaphore则是锁定一批资源。同时只允许指定数目的线程执行操作。
也与线程池有一定相似。不同的是线程池等待的线程未运行,而这个线程已经在运行并且在争夺。
适用于资源量小于线程量的情况。比如一个连接只允许同时有10个线程连接,现在有100个线程要执行这个连接操作,那么就可以用Semaphore进行控制。
常用方法
- Semaphore(int permits):构造方法,入参为许可量,即同时允许几个线程动作。默认为非公平的
- Semaphore(int permits, boolean fair):构造方法入参一为许可量,入参二为是否公平竞争 true为公平
- void acquire() throws InterruptedException:去获得一个许可,获得不到就一直等。等待过程中被中断报InterruptedException
- void acquireUninterruptibly():去获得一个许可,获得不到就一直等,无视中断命令
- boolean tryAcquire(): 尝试获得一个许可,如果获得就返回true,没获得就返回false
- boolean tryAcquire(long timeout, TimeUnit unit): 在指定时间里尝试获得一个许可,如果获得就返回true,没获得就返回false
- void release(): 释放一个许可,无需一定持有许可。即没有持有许可依然可以发出许可释放指令
- void acquire(int permits) throws InterruptedException:去获得指定数目的许可,获得不到就一直等。等待过程中被中断报InterruptedException
- void acquireUninterruptibly(int permits):去获得指定数目的许可,获得不到就一直等,无视中断命令
- boolean tryAcquire(int permits):去获得指定数目的许可,如果获得就返回true,没获得就返回false
- boolean tryAcquire(int permits, long timeout, TimeUnit unit):在指定时间里尝试获得指定数目的许可,如果获得就返回true,没获得就返回false
- void release(int permits): 释放指定数目的许可,无需一定持有指定数目许可。即使未拥有许可或者只拥有一个许可,依然可以释放两个甚至更多的许可。
- int availablePermits(): 当前可用许可量
- int drainPermits():获得所有的许可并返回获得的许可量
- boolean isFair():是否为公平竞争
- final boolean hasQueuedThreads():当前是否有线程在竞争
- final int getQueueLength():当前竞争队列的长度
示例
package sync;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Semaphore;
public class SemaphoreTest {
public static void main(String[] args) {
final Semaphore s = new Semaphore(2); // 入参为2则为正常的3任务调2个许可流程,为1可验证无需获得许可便可释放许可
Runnable r1 = new Runnable() {
@Override
public void run() {
try {
System.out.println("1号请求资源 - " + showNow());
s.acquire();
System.out.println("1号获得资源 - " + showNow());
System.out.println("1号开始工作 - " + showNow());
Thread.sleep(5000);
System.out.println("1号工作完毕,开始释放资源 - " + showNow());
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable r2 = new Runnable() {
@Override
public void run() {
try {
// Thread.sleep(200); // 保证执行顺序是1.2.3
System.out.println("2号请求资源 - " + showNow());
s.acquire(); // 如果把这个地方注释掉,并打开保证释放顺序的代码并设置许可为一个,运行会发现1号先持有资源工作,然后2无需获得许可就可释放许可,使得3在1未执行完毕释放许可时便获得许可开始执行
System.out.println("2号获得资源 - " + showNow());
System.out.println("2号开始工作 - " + showNow());
Thread.sleep(1000);
System.out.println("2号工作完毕,开始释放资源 - " + showNow());
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
Runnable r3 = new Runnable() {
@Override
public void run() {
try {
// Thread.sleep(500);// 保证执行顺序是1.2.3
System.out.println("3号请求资源 - " + showNow());
s.acquire();
System.out.println("3号获得资源 - " + showNow());
System.out.println("3号开始工作 - " + showNow());
Thread.sleep(1000);
System.out.println("3号工作完毕,开始释放资源 - " + showNow());
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
new Thread(r1).start();
new Thread(r2).start();
new Thread(r3).start();
}
private static String showNow(){
SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss.SSS");
return sdf.format(new Date());
}
}
运行结果:
2号请求资源 - 17:23:48.107
1号请求资源 - 17:23:48.107
2号获得资源 - 17:23:48.107
3号请求资源 - 17:23:48.107
2号开始工作 - 17:23:48.107
1号获得资源 - 17:23:48.107
1号开始工作 - 17:23:48.107
2号工作完毕,开始释放资源 - 17:23:49.107
3号获得资源 - 17:23:49.108
3号开始工作 - 17:23:49.108
3号工作完毕,开始释放资源 - 17:23:50.108
1号工作完毕,开始释放资源 - 17:23:53.107