关于java:判断线程池是否全部完成的-5-种方法还有谁不会

30次阅读

共计 4471 个字符,预计需要花费 12 分钟才能阅读完成。

起源:https://blog.csdn.net/m0_4614…

概述

最近写小玩具的时候用到了 CountDownLatch 计数器,而后顺便想了想判断线程池全副完结有多少种办法。

在网上搜了下,可能有些没找到,然而我找到的有(所有办法都是在 ThreadPoolExecutor 线程池办法下测试的):

  • isTerminated() 判断形式,在执行 shutdown(),敞开线程池后,判断是否所有工作曾经实现。
  • ThreadPoolExecutorgetCompletedTaskCount() 办法,判断实现工作数和全副工作数是否相等。
  • CountDownLatch 计数器,应用闭锁计数来判断是否全副实现。
  • 手动保护一个公共计数,原理和闭锁相似,就是更加灵便。
  • 应用 submit 向线程池提交工作,Future 判断工作执行状态。

好嘞,当初开始一个一个介绍优缺点和简要原理;

先创立一个 static 线程池,前面好几个例子就不一一创立了,全副用这个就行了:

/**
 * 创立一个最大线程数是 20 的线程池
 */
public static ThreadPoolExecutor pool = new ThreadPoolExecutor(
     10, 20, 0L,
     TimeUnit.MILLISECONDS,
     new LinkedBlockingQueue<>());

而后再筹备一个通用的睡眠办法:

/**
 * 线程执行办法,随机期待 0 到 10 秒
 */
private static void sleepMtehod(int index){
    try {long sleepTime = new Double(Math.random() * 10000).longValue();
        Thread.sleep(sleepTime);
        System.out.println("以后线程执行完结:" + index);
    } catch (InterruptedException e) {e.printStackTrace();
    }
}

这个办法就是为了测试的时候辨别线程执行结束的下程序而已。

好嘞,筹备结束,当初开始。

isTerminated 形式

首先贴上测试代码:

private static void shutdownTest() throws Exception {for (int i = 0; i < 30; i++) {
        int index = i;
        pool.execute(() -> sleepMtehod(index));
    }
    pool.shutdown();
    while (!pool.isTerminated()){Thread.sleep(1000);
        System.out.println("还没进行。。。");
    }
    System.out.println("全副执行结束");
}

这一种形式就是在主线程中进行循环判断,全副工作是否曾经实现。

这里有两个次要办法:

  • shutdown():启动有序敞开,其中先前提交的工作将被执行,但不会承受任何新工作。如果曾经敞开,调用没有额定的作用。
  • isTerminated():如果所有工作在敞开后实现,则返回 true。请留神,isTerminated 从不是 true,除非 shutdownshutdownNow 先被执行。

艰深点讲,就是在执行全副工作后,对线程池进行 shutdown() 有序敞开,而后循环判断 isTerminated(),线程池是否全副实现。

  • 长处:操作简略,代码更加简略。
  • 毛病:须要敞开线程池。个别我在代码中都是将线程池注入到 Spring 容器,而后各个组件中对立用同一个,当然不能敞开。

相似办法扩大:

  • shutdownNow():尝试进行所有被动执行的工作,进行期待工作的解决,并返回正在期待执行的工作列表。从此办法返回时,这些工作将从工作队列中删除。通过 Thread.interrupt() 勾销工作。
  • isShutdown():如果线程池已敞开,则返回 true。
  • isTerminating():如果在 shutdown()shutdownNow() 之后终止,但尚未齐全终止,则返回 true。
  • waitTermination(long timeout, TimeUnit unit):以后线程阻塞,直到等所有已提交的工作(包含正在跑的和队列中期待的)执行完,或者等超时工夫到,或者线程被中断抛出异样;全副执行完返回 true,超时返回 false。也能够用这个办法代替 isTerminated() 进行判断。

getCompletedTaskCount

还是一样,贴上代码:

private static void taskCountTest() throws Exception {for (int i = 0; i < 30; i++) {
            int index = i;
            pool.execute(() -> sleepMtehod(index));
        }
        // 当线程池实现的线程数等于线程池中的总线程数
        while (!(pool.getTaskCount() == pool.getCompletedTaskCount())) {System.out.println("工作总数:" + pool.getTaskCount() + ";曾经实现工作数:" + pool.getCompletedTaskCount());
            Thread.sleep(1000);
            System.out.println("还没进行。。。");
        }
        System.out.println("全副执行结束");
    }

还是一样在主线程循环判断,次要就两个办法:

  • getTaskCount():返回打算执行的工作总数。因为工作和线程的状态可能在计算过程中动态变化,因而返回的值只是一个近似值。
  • getCompletedTaskCount():返回实现执行的工作的大抵总数。因为工作和线程的状态可能在计算过程中动静地扭转,所以返回的值只是一个近似值,然而在间断的调用中并不会缩小。

这个好了解,总任务数等于已实现工作数,就示意全副执行结束。

  • 长处:齐全应用了 ThreadPoolExecutor 提供的办法,并且不用敞开线程池,防止了创立和销毁带来的损耗。
  • 毛病:下面的解释也看到了,应用这种判断存在很大的限度条件;必须确定,在循环判断过程中,没有新的工作产生。差不多意思就是,这个线程池只能在这条线程中应用。

其余:

最初扯两句,因为我用 main 办法运行的,跑完后 main 没有完结,是因为非守护线程如果不终止,程序是不会完结的。而线程池 Worker 线程里写了一个死循环,而且被设置成了非守护线程。

CountDownLatch 计数器

这种办法是我比拟罕用的办法,先看代码:

private static void countDownLatchTest() throws Exception {
     // 计数器,判断线程是否执行完结
     CountDownLatch taskLatch = new CountDownLatch(30);
     for (int i = 0; i < 30; i++) {
         int index = i;
         pool.execute(() -> {sleepMtehod(index);
             taskLatch.countDown();
             System.out.println("以后计数器数量:" + taskLatch.getCount());
         });
     }
     // 以后线程阻塞,期待计数器置为 0
     taskLatch.await();
     System.out.println("全副执行结束");
 }

这种办法,呃,应该是看起来比拟高级的,我也不晓得别的大佬怎么写的,反正我就用这个。

这个办法须要介绍下这个工具类 CountDownLatch。先把这种形式的优缺点写了,前面再具体介绍这个类。

  • 长处:代码优雅,不须要对线程池进行操作,将线程池作为 Bean 的状况下有很好的应用场景。
  • 毛病:须要提前晓得线程数量;性能的确,呃呃呃呃呃,差了点。哦对了,还须要在线程代码块内加上异样判断,否则在 countDown 之前产生异样而没有解决,就会导致主线程永远阻塞在 await。

CountDownLatch 概述

CountDownLatch 是 JDK 提供的一个同步工具,它能够让一个或多个线程期待,始终等到其余线程中执行实现一组操作。

罕用的办法有 countDown 办法和 await 办法,CountDownLatch 在初始化时,须要指定用给定一个整数作为计数器。

当调用 countDown 办法时,计数器会被减 1;当调用 await 办法时,如果计数器大于 0 时,线程会被阻塞,始终到计数器被 countDown 办法减到 0 时,线程才会继续执行。

计数器是无奈重置的,当计数器被减到 0 时,调用 await 办法都会间接返回。

保护一个公共计数

这种形式其实和 CountDownLatch 原理相似。

先保护一个动态变量

private static int taskNum = 0;

而后在线程工作完结时,进行动态变量操作:

private static void staticCountTest() throws Exception {Lock lock = new ReentrantLock();
     for (int i = 0; i < 30; i++) {
         int index = i;
         pool.execute(() -> {sleepMtehod(index);
             lock.lock();
             taskNum++;
             lock.unlock();});
     }
     while(taskNum < 30) {Thread.sleep(1000);
         System.out.println("还没进行。。。以后实现工作数:" + taskNum);
     }
     System.out.println("全副执行结束");
 }

其实就是加锁计数,循环判断。

  • 长处:手动保护形式更加灵便,对于一些非凡场景能够手动解决。
  • 毛病:和 CountDownLatch 相比,一样须要晓得线程数目,然而代码实现比拟麻烦,绝对于灵便这一个劣势,貌似投入产出并不对等。

Future 判断工作执行状态

Future 是用来装载线程后果的,不过,用这个来进行判断写代码总感觉怪怪的。

因为 Future 只能装载一条线程的返回后果,多条线程总不能用 List 在接管 Future。

这里就开一个线程做个演示:

private static void futureTest() throws Exception {Future<?> future = pool.submit(() -> sleepMtehod(1));
    while (!future.isDone()){Thread.sleep(500);
        System.out.println("还没进行。。。");
    }
    System.out.println("全副执行结束");
}

这种形式就不写优缺点了,因为 Future 的次要应用场景并不是用于判断工作执行状态。

近期热文举荐:

1.1,000+ 道 Java 面试题及答案整顿 (2022 最新版)

2. 劲爆!Java 协程要来了。。。

3.Spring Boot 2.x 教程,太全了!

4. 别再写满屏的爆爆爆炸类了,试试装璜器模式,这才是优雅的形式!!

5.《Java 开发手册(嵩山版)》最新公布,速速下载!

感觉不错,别忘了顺手点赞 + 转发哦!

正文完
 0