版权声明:本文出自汪磊的博客,转载请务必注明出处。

Java线程池技术属于比较“古老”而又比较基础的技术了,本篇博客主要作用是个人技术梳理,没什么新玩意。

一、Java线程池技术的由来

我们平时使用线程来进行异步操作时,线程的创建,销毁等相对来说都是比较消耗资源的,试想这样一个业务情景:高并发请求,但是每次请求的时间非常短。如果我们为每一个请求都单独创建一个线程来执行,就会消耗大量设备资源,使设备处于高负荷状态,显然这样的处理就有很大问题了。这时候我们就可以用线程池技术来解决了,我们在线程池中创建若干条线程,当有任务需要执行时就从该线程池中获取一个线程来执行任务,如果一时间任务过多,超出线程池的线程数量,那么后面的线程任务就进入一个等待队列进行等待,直到线程池有线程处于空闲时才从等待队列获取要执行的任务进行处理,这样就减少了线程创建和销毁的开销,实现了线程的复用。

二、Executor框架介绍

首先对整体框架有个大概了解,如图:

Executor是个接口,就定义了一个void execute(Runnable command)方法。

ExecutorService同样是一个接口并且继承自Executor接口,对其方法进行扩展,其中最重要的是<T> Future<T> submit(Callable<T> task)方法,关于Callable,Future,FutureTask不是本篇重点,有时间会单独写一篇博客介绍。也可以自行搜索了解,比较简单。

AbstractExecutorService抽象类,实现了ExecutorService接口,主要实现了submit,ivokeAny方法。

ScheduledExecutorService同样是一个接口,继承自ExecutorService接口,对其进行扩展,主要就是schedule等方法。

ThreadPoolExecutor具体线程池实现类,继承自AbstractExecutorService抽象类,我们使用的时候大部分就是使用这个类,后面会具体讲到。

ScheduledThreadPoolExecutor 具有调度能力的线程池实现类,继承自ThreadPoolExecutor类,且实现ScheduledExecutorService接口,其主要功能就是调用schedule方法,可以延时或者周期的执行某一任务,而ThreadPoolExecutor是没有这一共能的。

三、ThreadPoolExecutor构造函数参数介绍

在我们使用ThreadPoolExecutor的时候会发现构造函数中有很多参数,如下:

 public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
} public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
} public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
} public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}

前3个构造函数都是调用第4个构造函数,只不过有些参数使用默认的罢了。

接下来我们看下第4个构造函数每个参数意义:

public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,RejectedExecutionHandler handler)

参数 说明
corePoolSize 线程池中核心线程数量
maximumPoolSize 线程池中最大线程数量
keepAliveTime 非核心线程存活时间
unit keepAliveTime的时间单位
workQueue 存放任务的队列
threadFactory 用来生产线程的工厂
handler 当线程池中不能再放入任务时执行的handler

如果有一个corePoolSize为5,maximumPoolSize为10的线程池,可用下图形象展示:

这里要说明一下:所谓核心线程非核心线程只是一个数量的说明,并不是说核心线程非核心线程有本质上的不同,它们都是普通的线程而已,并且线程特性都一样,不是说核心线程有特殊标记,线程池能“认”出来这是核心线程,对其有特殊操作。

四、线程池处理任务的策略

我们调用线程池的submit()或execute()方法,向线程池中放入一个任务执行的时候线程池到底是怎么按照其策略来执行的呢?接下来,我们对其执行策略进行详细介绍,介绍完会对构造函数中每个参数有更深刻印象的。

1,调用线程池的submit()或execute()方法向线程池中放入一个任务,线程池内部会检查运行的线程数量是否达到corePoolSize数量,如果没有达到,则创建一个线程执行放入的任务,不管已经创建的线程是否处于空闲状态,创建线程的任务由threadFactory来完成,关于ThreadFactory可以参考我的另一篇博客来学习:java线程池技术(一):ThreadFactory与BlockingQueue。

2,我们继续向线程池中放入任务,此时线程池中运行的线程数量已经达到corePoolSize数量,则新加入的任务将会被放入workQueue中,直到有线程处于空闲状态,则从workQueue中取出任务执行。

3,继续向线程池中放入任务,此时线程池中运行的线程数量已经达到corePoolSize数量,并且workQueue中已经放满任务不能再放入新的任务,那么这时候就继续创建新的线程,注意此时线程池中线程数量已经多余corePoolSize数量,多出来的线程就叫做非核心线程。用非核心线程来执行新放入的任务。当线程池中的线程超过你设置的corePoolSize参数,说明当前线程池中有所谓的“非核心线程”。当某个线程处理完任务后,如果等待keepAliveTime时间后仍然没有新的任务分配给它,那么这个线程将会被回收。线程池回收线程时,对所谓的“核心线程”和“非核心线程”是一视同仁的,直到线程池中线程的数量等于你设置的corePoolSize参数时,回收过程才会停止。

4, 继续向线程池中放入任务,此时线程池中运行的线程数量已经达到maximumPoolSize数量,并且workQueue中已经放满任务不能再放入新的任务,由于线程池中运行的线程

已经达到maximumPoolSize数量,所以无法再创建线程执行新放入的任务,此时handler参数就起作用了,在使用的时候相信大部分开发者都没用过这个参数,我们看下系统默认怎么处理的,

系统默认闯入传入的是defaultHandler,如下:

 public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}

初始化如下:

     private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();

接下来看下AbortPolicy这个类吧:

     public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { } /**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}

AbortPolicy实现了RejectedExecutionHandler接口,在rejectedExecution方法中抛出RejectedExecutionException异常。

所以如果线程池中运行的线程数量已经达到maximumPoolSize数量,并且workQueueworkQueue中已经放满任务不能再放入新的任务,系统默认情况下就会抛出

RejectedExecutionException异常,我们也可以自己实现RejectedExecutionHandler接口,在rejectedExecution方法中实现自己策略。比如我自己写的网络请求框架就自己定义了

RejectedExecutionHandler,如下:

 public class RejectedPolicy implements RejectedExecutionHandler{

     @Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
try {
taskQuene.put(r);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

好了,以上就是线程池具体执行一个新任务的大体策略,是不是有了更深的认识???

以上分析中涉及的ThreadFactory与BlockingQueue如果你不是太了解,可以参考我的另一篇博客了解一下:java线程池技术(一):ThreadFactory与BlockingQueue。

好了,关于线程池大体介绍就到此为止,希望对你有用。

java线程池技术(二): 核心ThreadPoolExecutor介绍的更多相关文章

  1. Java 线程池(二)

    简介 在上篇 Java 线程池(一) 我们介绍了线程池中一些的重要参数和具体含义,这篇我们看一看在 Java 中是如何去实现线程池的,要想用好线程池,只知其然是远远不够的,我们需要深入实现源码去了解线 ...

  2. Java线程池中的核心线程是如何被重复利用的?

    真的!讲得太清楚了!https://blog.csdn.net/MingHuang2017/article/details/79571529 真的是解惑了 本文所说的"核心线程". ...

  3. juc线程池原理(二):ThreadPoolExecutor的成员变量介绍

    概要 线程池的实现类是ThreadPoolExecutor类.本章,我们通过分析ThreadPoolExecutor类,来了解线程池的原理. ThreadPoolExecutor数据结构 Thread ...

  4. java线程池技术

    1.线程池的实现原理?简介: 多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力.假设一个服务器完成一项任务所需时间为:T1 创建线程时间, ...

  5. 线程池技术之:ThreadPoolExecutor 源码解析

    java中的所说的线程池,一般都是围绕着 ThreadPoolExecutor 来展开的.其他的实现基本都是基于它,或者模仿它的.所以只要理解 ThreadPoolExecutor, 就相当于完全理解 ...

  6. Java线程池技术以及实现

    对于服务端而言,经常面对的是客户端传入的短小任务,需要服务端快速处理并返回结果.如果服务端每次接受一个客户端请求都创建一个线程然后处理请求返回数据,这在请求客户端数量少的阶段看起来是一个不错的选择,但 ...

  7. 线程池系列二:ThreadPoolExecutor讲解

    一.简介 1)线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为: ThreadPoolExecutor(int corePoolSize, i ...

  8. java线程池技术(一):ThreadFactory与BlockingQueue

    版权声明:本文出自汪磊的博客,转载请务必注明出处. 一.ThreadFactory概述以及源码分析 ThreadFactory很简单,就是一个线程工厂也就是负责生产线程的,我们看下ThreadFact ...

  9. java线程池系列(1)-ThreadPoolExecutor实现原理

    前言 做java开发的,一般都避免不了要面对java线程池技术,像tomcat之类的容器天然就支持多线程. 即使是做偏后端技术,如处理一些消息,执行一些计算任务,也经常需要用到线程池技术. 鉴于线程池 ...

随机推荐

  1. Databricks缓存提升Spark性能--为什么NVMe固态硬盘能够提升10倍缓存性能(原创)

    我们兴奋的宣布Databricks缓存的通用可用性,作为统一分析平台一部分的 Databricks 运行时特性,它可以将Spark工作负载的扫描速度提升10倍,并且这种改变无需任何代码修改. 1.在本 ...

  2. 编译和解释性语言和python运行方式

    1.编译型语言和解释性语言 编译型语言:在执行之前需要一个专门的编译过程,把程序编译成为机器语言的文件,运行时不需要重新翻译,直接使用编译的结果就行了.程序执行效率高,依赖编译器,跨平台性差些.如C. ...

  3. [DeeplearningAI笔记]ML strategy_1_1正交化/单一数字评估指标

    机器学习策略 ML strategy 觉得有用的话,欢迎一起讨论相互学习~Follow Me 1.1 什么是ML策略 机器学习策略简介 情景模拟 假设你正在训练一个分类器,你的系统已经达到了90%准确 ...

  4. 23_迭代器、模拟For循环

    一.可迭代对象 和 迭代器 1.可迭代对象和迭代器 可迭代对象:可以直接作用于for循环的对象统称为可迭代对象,Iterable. 迭代器:可以被next()函数调用并不断返回下一个值的对象称为迭代器 ...

  5. 【django之form表单】

    一.构建一个表单 假设你想在你的网站上创建一个简单的表单,以获得用户的名字.你需要类似这样的模板: <form action="/your-name/" method=&qu ...

  6. 看雪.TSRC 2017CTF秋季赛第三题

    看雪.TSRC 2017CTF秋季赛第三题 wp 这是一道很简单的题,反调试的坑略多.这道题采用了很多常用的反调试手段,比如调用IsDebuggerPresent.进程名检查等等.另外也有利用SEH的 ...

  7. root cause org.apache.ibatis.ognl.OgnlException: source is null for getProperty(null, "XXX")

    在执行一个查询语句的时候,mybatis报错:root cause org.apache.ibatis.ognl.OgnlException: source is null for getProper ...

  8. 《Python网络编程》学习笔记--从例子中收获的计算机网络相关知识

    从之前笔记的四个程序中(http://www.cnblogs.com/take-fetter/p/8278864.html),我们可以看出分别使用了谷歌地理编码API(对URL表示地理信息查询和如何获 ...

  9. document.activeElement 过滤选择文件弹窗导致的页面失焦

    在线考试页面,常常需要检测用户是否作弊. 一般是监听页面是否失焦的方式,而失焦的方式有很多种,比如QQ弹窗,切换页面,切换程序,input文件上传选择文件等 选择文件是正常情况,这种情况下需要过滤 本 ...

  10. xBIM WeXplorer xViewer 基本应用

    目录 基础 xBIM WeXplorer 简要介绍 xBIM WeXplorer xViewer 基本应用 xBIM WeXplorer xViewer 浏览器检查 xBIM WeXplorer xV ...