今天刚刚学习了Semaphore, 也就是我们说的信号量机制,用来控制某种资源的操作数量, Semaphore中管理着一组虚拟的许可, 可是通过构造函数初始化, 执行操作时首先要通过acquire方法申请许可, 如果没有则会阻塞(直到中断或者超时), release()方法释放虚拟许可. tryAcquire()方法中如果获取许可失败, 则立马返回, 不会阻塞. 同一个线程可以多次获取许可, 归还时也要归还多次.
场景:
虚拟许可为1时, 可用于资源的互斥操作
用于资源池的实现
碰到这个问题, 刚好来练习一下
// 定义方法接口
interface Method{
void apply();
}
public class MethodCounter {
Semaphore semaphore;
public MethodCounter(int threadNum) {
if(threadNum <= 0){
throw new IllegalArgumentException();
}
semaphore = new Semaphore(threadNum);
}
void restrict(Method method) throws InterruptedException {
semaphore.acquire(); // 获得许可失败, 则阻塞当前线程
/*if(!semaphore.tryAcquire()){
return; // 获取许可失败, 直接返回, 其他线程直接调用失败
}*/
method.apply(); // 执行方法
semaphore.release(); // 归还许可
}
// test方法
public static void main(String[] args) throws InterruptedException {
MethodCounter methodCounter = new MethodCounter(2);
Method method = new Method() {
@Override
public void apply() {
try {
System.out.println("helloworld");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
for(int i = 0; i < 5; i++){
new Thread(() -> {
try {
methodCounter.restrict(method);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}
再次思考, 上面的代码真的没问题吗? 既然Semaphore控的是资源, 可是现在我们是在控制线程啊? 线程和资源是一个东西吗? 如果一个线程只能获得一个许可, 那自然没问题, 但是一个线程如果可以获得多个许可的话, 那上面的代码就存在问题, 具体来说, 就是用户自定义方法里面存在递归, 一个线程把所有的许可都获得了, 但是这时只有一个线程. 这很想重入锁, 为了解决这个问题, 我需要给线程做标记, 如何作, 常见的方法就是ThreadLocal类, 天然的线程和值的映射关系
// 定义方法接口
interface Method{
void apply();
}
public class MethodCounter {
Semaphore semaphore;
ThreadLocal<Boolean> hasPermissionThreadLocal; // 用来存储当前线程是否已经有了许可
public MethodCounter(int threadNum) {
if(threadNum <= 0){
throw new IllegalArgumentException();
}
semaphore = new Semaphore(threadNum);
hasPermissionThreadLocal = new ThreadLocal<>();
}
void restrict(Method method) throws InterruptedException {
Boolean hasPermission = hasPermissionThreadLocal.get();
// 判断当前线程是否获得过许可, 如果没有则获得许可
if(hasPermission == null || !hasPermission) {
semaphore.acquire();
hasPermissionThreadLocal.set(true);
}
// 执行业务方法
method.apply();
// 归还许可, 同时置当前线程尚未
semaphore.release();
hasPermissionThreadLocal.remove();
}
// test方法
public static void main(String[] args) throws InterruptedException {
MethodCounter methodCounter = new MethodCounter(2);
Method method = new Method() {
@Override
public void apply() {
try {
System.out.println("helloworld");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
apply();
}
};
for(int i = 0; i < 5; i++){
new Thread(() -> {
try {
methodCounter.restrict(method);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
}