如何控制某个方法允许并发访问线程的个数

今天刚刚学习了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();
            }
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值