前言

平时接触过多线程开发的童鞋应该都或多或少了解过线程池,之前发布的《阿里巴巴 Java 手册》里也有一条:

可见线程池的重要性。

简单来说使用线程池有以下几个目的:

  • 线程是稀缺资源,不能频繁的创建。
  • 解耦作用;线程创建于执行完全分开,方便维护
  • 应当将其放入一个池子中,可以给其他任务进行复用。

线程池原理

谈到线程池就会想到池化技术,其中最核心的思想就是把宝贵的资源放到一个池子中;每次使用都从里面获取,用完之后又放回池子供其他人使用,有点吃大锅饭的意思。

那在 Java 中又是如何实现的呢?

在 JDK 1.5 之后推出了相关的 api,常见的创建线程池方式有以下几种:

  • Executors.newCachedThreadPool():无限线程池。
  • Executors.newFixedThreadPool(nThreads):创建固定大小的线程池。
  • Executors.newSingleThreadExecutor():创建单个线程的线程池。

其实看这三种方式创建的源码就会发现:

public static ExecutorService newCachedThreadPool() {  return new ThreadPoolExecutor(0, Integer.MAX_VALUE,  60L, TimeUnit.SECONDS,  new SynchronousQueue());  } 

实际上还是利用 ThreadPoolExecutor 类实现的。

所以我们重点来看下:ThreadPoolExecutor 是怎么玩的。

首先是创建线程的 api:

ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler) 

这几个核心参数的作用:

  • corePoolSize 为线程池的基本大小。
  • maximumPoolSize 为线程池最大线程大小。
  • keepAliveTime 和 unit 则是线程空闲后的存活时间。
  • workQueue 用于存放任务的阻塞队列。
  • handler 当队列和最大线程池都满了之后的饱和策略。

了解了这几个参数再来看看实际的运用。

通常我们都是使用:

threadPool.execute(new Job()); 

这样的方式来提交一个任务到线程池中,所以核心的逻辑就是 execute() 函数了。

在具体分析之前先了解下线程池中所定义的状态,这些状态都和线程的执行密切相关:

  • RUNNING 自然是运行状态,指可以接受任务执行队列里的任务
  • SHUTDOWN 指调用了 shutdown() 方法,不再接受新任务了,但是队列里的任务得执行完毕。
  • STOP 指调用了 shutdownNow() 方法,不再接受新任务,同时抛弃阻塞队列里的所有任务并中断所有正在执行任务。
  • TIDYING 所有任务都执行完毕,在调用 shutdown()/shutdownNow() 中都会尝试更新为这个状态。
  • TERMINATED 终止状态,当执行 terminated() 后会更新为这个状态。

用图表示为:

然后看看 execute() 方法是如何处理的:

  • 获取当前线程池的状态。
  • 当前线程数量小于 coreSize 时创建一个新的线程运行。
  • 如果当前线程处于运行状态,并且写入阻塞队列成功。
  • 双重检查,再次获取线程状态;如果线程状态变了(非运行状态)就需要从阻塞队列移除任务,并尝试判断线程是否全部执行完毕。同时执行拒绝策略。
  • 如果当前线程池为空就新创建一个线程并执行。
  • 如果在第三步的判断为非运行状态,尝试新建线程,如果失败则执行拒绝策略。

这里借助《聊聊并发》的一张图来描述这个流程:

如何配置线程?

流程聊完了再来看看上文提到了几个核心参数应该如何配置呢?

有一点是肯定的,线程池肯定是不是越大越好。

通常我们是需要根据这批任务执行的性质来确定的。

  • IO 密集型任务:由于线程并不是一直在运行,所以可以尽可能的多配置线程,比如 CPU 个数 * 2
  • CPU 密集型任务(大量复杂的运算)应当分配较少的线程,比如 CPU 个数相当的大小。

当然这些都是经验值,最好的方式还是根据实际情况测试得出最佳配置。

优雅的关闭线程池

有运行任务自然也有关闭任务,从上文提到的 5 个状态就能看出如何来关闭线程池。

其实无非就是两个方法:

shutdown()/shutdownNow()。

但他们有着重要的区别:

  • shutdown() 执行后停止接受新任务,会把队列的任务执行完毕。
  • shutdownNow() 也是停止接受新任务,但会中断所有的任务,将线程池状态变为 stop。

两个方法都会中断线程,用户可自行判断是否需要响应中断。

shutdownNow() 要更简单粗暴,可以根据实际场景选择不同的方法。

我通常是按照以下方式关闭线程池的:

long start = System.currentTimeMillis();  for (int i = 0; i <= 5; i++) {  pool.execute(new Job());  }  pool.shutdown();  while (!pool.awaitTermination(1, TimeUnit.SECONDS)) {  LOGGER.info("线程还在执行。。。");  }  long end = System.currentTimeMillis();  LOGGER.info("一共处理了【{}】

判断线程是否执行完毕_关于线程池你不能不知道的东西相关推荐

  1. python 判断线程是否执行完毕_判断线程池中的线程是否全部执行完毕

    在使用多线程的时候有时候我们会使用 java.util.concurrent.Executors的线程池,当多个线程异步执行的时候,我们往往不好判断是否线程池中所有的子线程都已经执行完毕,但有时候这种 ...

  2. python判断线程结束_判断Threading.start新线程是否执行完毕的实例

    新写自己的Threading类 class MyThread(threading.Thread):#我的Thread类 判断流程结束没 用于os shell命令是否执行判断 def __init__( ...

  3. python判断线程是否执行完成_判断Threading.start新线程是否执行完毕的实例

    新写自己的Threading类 class MyThread(threading.Thread):#我的Thread类 判断流程结束没 用于os shell命令是否执行判断 def __init__( ...

  4. 判断线程是否执行完毕_Java并发编程 | 线程核心机制,基础概念扩展

    源码地址:GitHub || GitEE 一.线程基本机制 1.概念描述 并发编程的特点是:可以将程序划分为多个分离且独立运行的任务,通过线程来驱动这些独立的任务执行,从而提升整体的效率.下面提供一个 ...

  5. 假如有Thread1、Thread2、ThreaD3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现?...

    有两种方法: 第一种方法: 一般情况,我们实现多线程都是Thread或者Runnable(后者比较多),但是,这两种都是没返回值的,所以我们需要使用callable(有返回值的多线程)和future( ...

  6. 关于假如有Thread1、Thread2、Thread3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现?

    最近看了一些面试题,然后发现这个频率比较高,然后就细心地了解下 在这里主要想讲解三种方法(毕竟还有其他方法): 一.使用join方法: 这个是辅助类: package com.com.aaa.five ...

  7. 假如有Thread1、Thread2、ThreaD3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现?

    一般情况,我们实现多线程都是Thread或者Runnable(后者比较多),但是,这两种都是没返回值的,所以我们需要使用callable(有返回值的多线程)和future(获得线程的返回值)来实现了. ...

  8. c# ThreadPool 判断子线程全部执行完毕的四种方法

    1.先来看看这个 多线程编程 多线程用于数据采集时,速度明显很快,下面是基本方法,把那个auto写成采集数据方法即可. using System; using System.Collections.G ...

  9. java线程中的常用方法_[多线程] 线程中的常用方法-最详细

    线程中常用的方法 1.public void start() 使该线程开始执行:Java 虚拟机调用该线程的 run 方法. 2.public void run() 如果该线程是使用独立的 Runna ...

最新文章

  1. linux各文件夹的作用域
  2. Python 比特币 教程 之一:创建机器人
  3. Java中的main()方法详解
  4. 【干货分享】云服务平台的架构及优势(上)
  5. 阿里云配置tornado无法访问
  6. mysql主从数据丢失_故障分析 | 记一次 MySQL 主从双写导致的数据丢失问题
  7. QQ 邮箱设置自定义域名邮箱
  8. IIS建立FTP站点
  9. 几种常见简易的恒流源电路
  10. 【vscode简单入门(四)】vscode精美UI主题推荐 (~」还你一个花里胡哨的vscode「~)
  11. 转]自己开心一下!!!很轻松的~
  12. RGB灯控圣诞灯七彩灯方案开发IC方案控制板供应详细介绍
  13. 晨之替——谷川 俊太郎
  14. 原生js实现动态数据表格
  15. 中国雅虎——长在阿里巴巴脸上的青春痘
  16. 记一次confluence故障的RCA
  17. 哈工大软件构造Lab2实验
  18. 【踩坑专栏】Field xxFeignClient in xxx required a bean of type ‘xxx.XXFeignClient‘ that could not be found
  19. 观李筱懿视频号有感:不要让所谓的大度变成对自己的道德绑架
  20. mac 连接l2tp没反应解决方案

热门文章

  1. hp惠普笔记本电脑黑屏大小写锁定键闪烁报错无法开机怎么办?
  2. Python3和Raspberry Pi最全面最直接的课程
  3. Zbrush制作手榴弹案例讲解学习教程
  4. 递归/回溯:Subsets II求子集(有重复元素)
  5. 线程互斥和同步-- 互斥锁
  6. 使用相对路径时,./、../、../../,代表的什么?
  7. leetcode 179. 最大数
  8. LeetCode 228: Summary Ranges
  9. Flex实现页面多态--state对象
  10. WMI Series :事件预订和处理