1、Semaphore含义
Semaphore称为计数信号量,它允许n个任务同时访问某个资源,可以将信号量看做是在向外分发使用资源的许可证,只有成功获取许可证,才能使用资源。
2、Semaphore用法
示例:定义一组许可证,假设许可证数量为10,t1线程需要消费5个许可证,t1执行,t2线程需要消费6个许可证,当前许可证数量为(10-5=5)t2阻塞,只有t1释放之后,许可证数量大于等于6,t2执行。
3、Semaphore(信号量)主要方法使用说明
1.1、
public Semaphore(int permits) { sync = new NonfairSync(permits); }
该构造函数会创建具有给定的许可数 和 默认为非公平的许可证数量
1.2、
public Semaphore(int permits, boolean fair) { sync = fair ? new FairSync(permits) : new NonfairSync(permits); }
该构造函数会创建具有给定的许可数 和 可选择的是否公平 的许可证数量
2、
public void acquire(int permits) throws InterruptedException { if (permits < 0) throw new IllegalArgumentException(); sync.acquireSharedInterruptibly(permits); }
此方法从信号量获取一个(多个)许可,在提供一个许可前一直将线程阻塞,或者线程被中断
3、
public void release(int permits) { if (permits < 0) throw new IllegalArgumentException(); sync.releaseShared(permits); }
此方法释放一个(多个)许可,将其返回给信号量
4、使用Semaphore的例子
说明: 该例子使用3个线程检验许可证的数量是否满足线程中要求的情况下验证结果
/**
* Semaphore示例
*
* @author zl
*
*/
public class SemaphoreDemo {
// 初始设置为10个许可证
public final static int SEM_USE_SIZE = 10;
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(SEM_USE_SIZE);
// 创建两个线程执行
MyThread t1 = new MyThread("t1", semaphore);
MyThread t2 = new MyThread("t2", semaphore);
t1.start();
t2.start();
// 主线程执行需要的5个许可证
int permits = 5;
System.out.println(Thread.currentThread().getName() + " trying to acquire");
try {
// 主线程需要5个许可证
semaphore.acquire(permits);
System.out.println(Thread.currentThread().getName() + " acquire successfully");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 释放许可证
semaphore.release(permits);
System.out.println(Thread.currentThread().getName() + " release successfully");
}
}
}
class MyThread extends Thread {
private Semaphore semaphore;
public MyThread(String name, Semaphore semaphore) {
super(name);
this.semaphore = semaphore;
}
@Override
public void run() {
// 该线程执行需要3个许可证
int count = 3;
System.out.println(Thread.currentThread().getName() + " trying to acquire");
try {
// 如果许可证大于count,代表能够取到,线程执行,否则阻塞
semaphore.acquire(count);
System.out.println(Thread.currentThread().getName() + " acquire successfully");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 当线程执行完或者被打断后,归还许可证
semaphore.release(count);
System.out.println(Thread.currentThread().getName() + " release successfully");
}
}
}
其中某一次的运行结果
main trying to acquire //main线程开始获取许可
main acquire successfully //main线程成功获取许可,此时许可证数量为10-5=5
t1 trying to acquire //t1线程开始获取许可
t1 acquire successfully //t1线程成功获取许可,此时许可证数量为5-3=2
t2 trying to acquire //t2线程开始获取许可 ,t2需要3个许可,但总许可证只有2,阻塞
main release successfully //main线程结束,释放许可证,总许可证为2+5=7
t2 acquire successfully //许可证数量满足t2线程要求,t2线程成功获取许可
t1 release successfully
t2 release successfully
5、说明
1、semaphore初始化有1个令牌,1个线程调用一次acquire方法,然后调用两次release方法,之后另外一个线程调用acquire(2)方法,此线程能够获取到足够的令牌并继续运行吗?
答案:能,原因是release方法会添加令牌,并不会以初始化的大小为准
2、semaphore初始化有2个令牌,一个线程调用1次release方法,然后一次性获取3个令牌,会获取到吗?
答案:能,原因是release会添加令牌,并不会以初始化的大小为准。Semaphore中release方法的调用并没有限制要在acquire后调用。
3、semaphore初始化有10个令牌,11个线程同时各调用1次acquire方法,会发生什么?
答案:拿不到令牌的线程阻塞,不会继续往下运行。
4、semaphore初始化有10个令牌,一个线程重复调用11次acquire方法,会发生什么?
答案:线程阻塞,不会继续往下运行。可能你会考虑类似于锁的重入的问题,很好,但是,令牌没有重入的概念。你只要调用一次acquire方法,就需要有一个令牌才能继续运行
其中的内部逻辑及执行流程可参照JUC工具类: Semaphore详解 | Java 全栈知识体系