Java 多线程(七):Executor 线程池框架

迈不过友情╰ 2021-08-12 00:39 591阅读 0赞
  • 线程的频繁创建在高并发及大数据量时是非常消耗资源的
  • 因此 Java 提供了线程池,Java5 在 java.util.concurrent 中添加了 Exectuor 异步执行框架
  • 通过线程池减少线程创建和销毁的次数,重复利用,根据系统情况调整线程池数量,防止创建过多线程
  • java.util.concurrent 中包含了几个比较重要的类:





























类名 描述
Executor 线程池接口
ExecutorService 在基础 Executor 线程池接口上生命了生命周期管理方法、任务执行状况跟踪方法
ScheduledExecutorService 一个定时调度任务的接口
ScheduledThreadPoolExecutor ScheduledExecutorService 的实现,实现了可定时调度任务的线程池
ThreadPoolExecutor 线程池,可以通过调用 Executors 以下静态工厂方法来创建线程池并返回一个 ExecutorService 对象

ThreadPoolExecutor 构造函数

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue<Runnable> workQueue,
  6. ThreadFactory threadFactory,
  7. RejectedExecutionHandler handler) //后两个参数为可选参数
  • corePoolSize:核心线程数,如果运行的线程少于 corePoolSize 则创建新线程来执行新任务,即使线程池中的其他线程是空闲的
  • maximumPoolSize:最大线程数,corePoolSize和maximumPoolSize设置的边界自动调整池大小:

    • 当前线程数 >= corePoolSize(且任务队列已满时,线程会创建新线程来处理任务)
    • 当前线程数 = maxPoolSize(且任务队列已满时,线程池会拒绝处理任务而抛出异常)
  • keepAliveTime:如果线程数多于 corePoolSize 则这些多余的线程的空闲时间超过 keepAliveTime 时将被终止
  • unit:keepAliveTime 参数的时间单位
  • workQueue:保存任务的阻塞队列,与线程池的大小有关:

    • 当运行的线程数少于 corePoolSize 时,新任务直接创建新线程来执行任务而无需再进队列
    • 当运行的线程数等于或多于 corePoolSize 新任务添加时则选加入队列,不直接创建线程
    • 当队列满时,新任务就会创建新线程
  • threadFactory:使用 ThreadFactory 创建新线程,默认使用 defaultThreadFactory 创建线程
  • handle:定义处理被拒绝任务的策略,默认使用 ThreadPoolExecutor.AbortPolicy 任务被拒绝时将抛出 RejectExecutorException

Executors

  • Executors 提供了一系列静态工厂方法用于创建各种线程池,根据具体应用场景而选择不同的线程池

newFixedThreadPool

  • 创建可重用且固定线程数的线程池,如果线程池中的所有线程都处于活动状态,此时再提交任务就在队列中等待,直到有可用线程
  • 如果线程池中的某个线程由于异常而结束时,线程池就会再添加一个新线程

方法定义

  1. //使用一个基于FIFO排序的阻塞队列,在所有corePoolSize线程都忙时新任务将在队列中等待
  2. public static ExecutorService newFixedThreadPool(int nThreads) {
  3. return new ThreadPoolExecutor(nThreads, nThreads,
  4. 0L, TimeUnit.MILLISECONDS,
  5. new LinkedBlockingQueue<Runnable>());
  6. }

使用例子

  1. public class Main {
  2. public static void main(String[] args) {
  3. ExecutorService executorService = Executors.newFixedThreadPool(5);
  4. IntStream.range(0, 6).forEach(i -> executorService.execute(() -> {
  5. try {
  6. TimeUnit.SECONDS.sleep(1);
  7. String threadName = Thread.currentThread().getName();
  8. System.out.println("finished: " + threadName);
  9. } catch (InterruptedException e) {
  10. e.printStackTrace();
  11. }
  12. }));
  13. }
  14. }
  15. //输出结果为:
  16. //finished: pool-1-thread-1
  17. //finished: pool-1-thread-2
  18. //finished: pool-1-thread-3
  19. //finished: pool-1-thread-4
  20. //finished: pool-1-thread-5
  21. //finished: pool-1-thread-1

newSingleThreadExecutor

  • 创建一个单线程的 Executor,它只会用唯一的线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
  • 如果该线程因为异常而结束就新建一条线程来继续执行后续的任务

方法定义

  1. public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
  2. //corePoolSize和maximumPoolSize都等于,表示固定线程池大小为1
  3. return new FinalizableDelegatedExecutorService
  4. (new ThreadPoolExecutor(1, 1,
  5. 0L, TimeUnit.MILLISECONDS,
  6. new LinkedBlockingQueue<Runnable>(),
  7. threadFactory));
  8. }

使用例子

  1. public class Main {
  2. private static int num = 100;
  3. public static void main(String[] args) {
  4. ExecutorService executorService = Executors.newSingleThreadExecutor();
  5. IntStream.range(0, 100).forEach(i -> executorService.execute(() -> {
  6. String runnableName = "Runnable"+i+":";
  7. System.out.println(runnableName + num--);
  8. }));
  9. try {
  10. executorService.shutdown();
  11. executorService.awaitTermination(5, TimeUnit.SECONDS);
  12. } catch (InterruptedException e) {
  13. e.printStackTrace();
  14. } finally {
  15. if (!executorService.isTerminated()) {
  16. executorService.shutdownNow();
  17. }
  18. }
  19. System.out.println("执行结束");
  20. }
  21. }
  22. /** 输出结果为:
  23. Runnable0:100
  24. Runnable1:99
  25. Runnable2:98
  26. .....
  27. Runnable98:2
  28. Runnable99:1
  29. 执行结束
  30. **/

newScheduledThreadPool

  • 创建一个可延迟执行或定期执行的线程池

方法定义

  1. public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
  2. return new ScheduledThreadPoolExecutor(corePoolSize);
  3. }

使用例子

  1. public class Main {
  2. public static void main(String[] args) {
  3. ScheduledExecutorService executorService = Executors.newScheduledThreadPool(2);
  4. IntStream.range(0, 2).forEach(i -> executorService.scheduleAtFixedRate(() -> {
  5. String threadName = Thread.currentThread().getName();
  6. System.out.println("finished: " + threadName);
  7. },1000, 2000, TimeUnit.MILLISECONDS));
  8. }
  9. }
  10. //输出结果为:
  11. //finished: pool-1-thread-1
  12. //finished: pool-1-thread-2
  13. //finished: pool-1-thread-1
  14. //finished: pool-1-thread-2
  15. //finished: pool-1-thread-2
  16. //finished: pool-1-thread-1

newCachedThreadPool

  • 创建可缓存的线程池,如果线程池中的线程在 60 秒未被使用就将被移除,在执行新的任务时,当线程池中有之前创建的可用线程就重用可用线程,否则就新建一个线程

    public static ExecutorService newCachedThreadPool() {

    1. return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
    2. 60L, TimeUnit.SECONDS,
    3. new SynchronousQueue<Runnable>());

    }

    public class Main {

    1. public static void main(String[] args) {
    2. ExecutorService executorService = Executors.newCachedThreadPool();
    3. IntStream.range(0, 6).forEach(i -> executorService.execute(() -> {
    4. String threadName = Thread.currentThread().getName();
    5. System.out.println("finished: " + threadName);
    6. }));
    7. }

    }

    //输出结果为:
    //finished: pool-1-thread-1
    //finished: pool-1-thread-2
    //finished: pool-1-thread-2
    //finished: pool-1-thread-2
    //finished: pool-1-thread-2
    //finished: pool-1-thread-2
    //线程池会缓存线程,尽可能的利用线程资源

ExecutorService 常用方法

  • shutdown:方法等待提交的任务执行完成并不再接受新任务,在完成全部提交的任务后关闭
  • shutdownNow:方法将强制终止所有运行中的任务并不再允许提交新任务
  • awaitTermination:阻塞,直到所有任务在关闭请求之后完成执行,或发生超时,或当前线程中断
  • isTerminated:当所有任务关闭后返回 true,除非 shutdown、shutdownNow 被调用,否则永远不为 true
  • submit:提交可执行的任务(Runnable、Thread、Callable)并执行,返回一个 Future 对象

发表评论

表情:
评论列表 (有 0 条评论,591人围观)

还没有评论,来说两句吧...

相关阅读

    相关 线Executor 框架

    本节思维导图:  ![70][] 思维导图源文件+思维导图软件关注微信公众号:“Java面试通关手册” 回复关键字:“Java多线程” 免费领取。 一 使用线程池的好处