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

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. 【转】nagios使用带url的check_http检测主机

    前一段时间在Cu论坛发现一个提问,问题是nagios关于检测主机http服务的.原帖地址http://bbs.chinaunix.net /forum.php?mod=viewthread&t ...

  2. jenkins安装配置[二]

    标签(linux): jenkins 笔者Q:972581034 交流群:605799367.有任何疑问可与笔者或加群交流 安装依赖,如果本机已有java环境可跳过 yum install java- ...

  3. java 如何将 word,excel,ppt如何转pdf --openoffice (1)

    承上启下,可折叠 上一篇说的是:服务器是windows server时,用jacob将msoffice(指的是word,excel,ppt)转换成pdf. 若被部署项目的服务器是centOS等linu ...

  4. PyCharm配置autopep8,自动格式化Python代码

    1. 关于PEP 8 PEP 8,Style Guide for Python Code,是Python官方推出编码约定,主要是为了保证 Python 编码的风格一致,提高代码的可读性. 官网地址:h ...

  5. rapid framework开发系列(一)

    定义:web项目脚手架 rapid-framework是一个以spring为核心的项目脚手架(或者称为胶水框架),框架将各个零散的框架(struts,strust2,springmvc,hiberna ...

  6. java中Queue简介

    Queue: 基本上,一个队列就是一个先入先出(FIFO)的数据结构 offer,add区别:一些队列有大小限制,因此如果想在一个满的队列中加入一个新项,多出的项就会被拒绝.这时新的 offer 方法 ...

  7. 【IT人】如何提高阅读源代码的效率

    1.最近刚到公司,公司就发一架构代码自己看,看了几天看的想吐,也在网上找了下相关的技巧吧,不是有句话叫做:成功必有方法,失败总是借口! 2.借鉴别人的方法来看看如下: 记得在开源流行之前,我看过的代码 ...

  8. Nginx Rewrite规则详解

    Rewrite规则含义就是某个URL重写成特定的URL,从某种意义上说为了美观或者对搜索引擎友好,提高收录量及排名等. Rewrite规则的最后一项参数为flag标记,支持的flag标记主要有以下几种 ...

  9. Math Jax开源数学编辑器的使用

    首先,这是一个开源免费,同时也可以支持扩展的软件. 使用API文档: 中文网站(http://mathjax-chinese-doc.readthedocs.io/en/latest/index.ht ...

  10. PhpStorm使用之 —— Xdebug断点调试

    PhpStorm使用之 -- Xdebug断点调试 在<XAMPP的配置与使用>中已经阐述了Xdebug插件的配置,Xdebug配置完成后,只需要在IDE工具中进行相关设置,便可启动Xde ...