记一次开发中遇到的问题:限制线程执行的数量

现有一个接口,每次请求后他会开启一组线程去处理业务,线程执行完大概要耗时1个小时左右,并会在执行的过程中消耗一定的硬件资源,如果10秒钟内有10个请求过来就将有10个线程同时执行,这无疑会对服务器造成一定压力,所以现在要进行限制,而相比直接使用sentinel进行限流,这里将会在后端的线程执行上进行处理。

实现方式

这里首先想到的就是使用是否可以使用CountDownLatch 进行解决,初始化为零,然后A线程开始执行的时候把值设成1,执行完了就设置成0,在A还没结束的时候来了线程B就要进行等待。

但是会有两种意外情况,第一种是两个线程同时获得CountDownLatch 的值,这时就会两个线程一块执行了。 第二种是假如A线程执行期间挂掉了,就相当于CountDownLatch 的值没有设回零,B线程就要一直等待了,但好在 await方法可以设置超时时间。

 public boolean await(long timeout, TimeUnit unit)
     throws InterruptedException {
     return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
 }

但仍存在问题,假如A线程挂掉了,此时BCD三个线程正在等待,如果他们超时时间都设置的1小时,那么时间一过这 仨都会开始执行,这样肯定有问题,所以就要把他们的超时时间设置成递增的,如B超时时间设成1小时,C设成两小时,D设成三个小时。

其实写到这里我就发现代码已经用了很多行了,然后换了一种方案,使用队列。

public class Test{
    private static final AtomicReference<LinkedBlockingQueue<Integer>> LINKED_BLOCKING_QUEUE = new AtomicReference<>(new LinkedBlockingQueue<>(1));
    private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger(0);

....省略.....
     new Thread(threadGroup, () -> {
         //限制每次只有一个线程执行,超时后将继续执行,使超时时间自增能让超时等待的线程也能排队运行,确保只有一个在执行
         try {
             LINKED_BLOCKING_QUEUE.get().offer(1, ATOMIC_INTEGER.incrementAndGet(), TimeUnit.HOURS);
         } catch (InterruptedException e) {
             e.printStackTrace();
         } finally {
             ATOMIC_INTEGER.set(ATOMIC_INTEGER.decrementAndGet());
         }
....省略.....

使用阻塞队列,容量为1,ATOMIC_INTEGER 代表超时时间,基础值为1,
假设现在在线程A执行结束前会有线程BCD依次来执行, 单线A执行时队列还为空,所以不用等待,注意finally 块中来把超时时间置回零,线程B执行时开始阻塞,超时时间为1,线程C来时 也阻塞超时时间为2,D则递增到3。 这样就能尽最大限度来保证只有一个线程在执行。
线程执行结束时会把队列清空掉。

//要注意如果线程没有走到这一步就挂了 那么下次请求的时候即使没有线程在运行也会等待一定的时间
LINKED_BLOCKING_QUEUE.get().remove();

除了使用队列 还可以使用Jdk为我们提供的线程通讯工具类 Semaphore,它往往用于资源有限的场景中,去限制线程的数量。
如下面的例子,它可以限制始终只有三个线程在执行,其核心方法就是acquire() 和release(),用其限定资源同样可以达到需求。

public class SemaphoreDemo {
    static class MyThread implements Runnable {

        private final int value;
        private final Semaphore semaphore;

        public MyThread(int value, Semaphore semaphore) {
            this.value = value;
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire(); // 获取permit
                System.out.printf("当前线程是%d, 还剩%d个资源,还有%d个线程在等待%n",
                        value, semaphore.availablePermits(), semaphore.getQueueLength());
                // 睡眠随机时间,打乱释放顺序
                Random random = new Random();
                Thread.sleep(random.nextInt(1000));
                System.out.printf("线程%d释放了资源%n", value);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release(); // 释放permit
            }
        }
    }

    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3);
        for (int i = 0; i < 10; i++) {
            new Thread(new MyThread(i, semaphore)).start();
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值