JDK线程池
简介
多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力,但频繁的创建线程的开销是很大的,那么如何来减少这部分的开销了,那么就要考虑使用线程池了。线程池就是一个线程的容器,每次只执行额定数量的线程,线程池就是用来管理这些额定数量的线程
线程池相关类结构图
ExecutorService
继承了Executor
接口
Executor
接口中的定义:
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
The {@code Executor} implementations provided in this package
implement {@link ExecutorService}, which is a more extensive
interface. The {@link ThreadPoolExecutor} class provides an
extensible thread pool implementation. The {@link Executors} class
provides convenient factory methods for these Executors.
根据JDK源码中的注释:ExecutorService
是对Executor
扩展的一个接口,ThreadPoolExecutor
类提供了对ExecutorService
的实现,Executors
类提供了方便的工厂方法
如何创建一个线程池
使用Executors工厂类来创建
Executors提供了几种创建线程池的方法:
1) 创建固定大小的线程池newFixedThreadPool
@Test
public void test1(){
ExecutorService pool = Executors.newFixedThreadPool(5);
for (int i = 0;i<5;i++) {
Runnable task = new myTask();
pool.submit(task); // submit方法内部会调用execute方法
}
}
class myTask implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"正在执行........");
}
}
运行结果:
pool-1-thread-1正在执行........
pool-1-thread-2正在执行........
pool-1-thread-1正在执行........
pool-1-thread-2正在执行........
pool-1-thread-2正在执行........
pool-1-thread-2正在执行........
pool-1-thread-1正在执行........
pool-1-thread-3正在执行........
pool-1-thread-4正在执行........
pool-1-thread-5正在执行........
2) 单线程的线程池newSignleThreadExecutor
@Test
public void test2(){
ExecutorService pool = Executors.newSingleThreadExecutor();
for (int i = 0;i<10;i++) {
Runnable task = new myTask();
pool.submit(task);
}
}
运行结果:
pool-1-thread-1正在执行........
pool-1-thread-1正在执行........
pool-1-thread-1正在执行........
pool-1-thread-1正在执行........
pool-1-thread-1正在执行........
pool-1-thread-1正在执行........
pool-1-thread-1正在执行........
pool-1-thread-1正在执行........
pool-1-thread-1正在执行........
pool-1-thread-1正在执行........
单线程的线程池:这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行
3)newScheduledThreadPool
@Test
public void test3(){
ScheduledExecutorService pool = Executors.newScheduledThreadPool(5);
pool.schedule(new myTask(), 1000, TimeUnit.MILLISECONDS);
pool.schedule(new myTask(), 2000, TimeUnit.MILLISECONDS);
pool.shutdown();
try{
Thread.currentThread().join(5000);
}catch (Exception e) {
// TODO: handle exception
}
}
等待1s后
pool-1-thread-2正在执行........
等待1s后
pool-1-thread-1正在执行........
- 可以缓存的线程池
newCachedThreadPool
@Test
public void test4(){
ExecutorService pool = Executors.newCachedThreadPool();
for (int i = 0;i<100;i++) {
Runnable task = new myTask();
pool.submit(task);
}
}
运行结果
pool-1-thread-3正在执行........
pool-1-thread-2正在执行........
pool-1-thread-1正在执行........
pool-1-thread-5正在执行........
pool-1-thread-3正在执行........
pool-1-thread-2正在执行........
pool-1-thread-7正在执行........
pool-1-thread-8正在执行........
pool-1-thread-10正在执行........
pool-1-thread-9正在执行........
pool-1-thread-13正在执行........
pool-1-thread-14正在执行........
pool-1-thread-16正在执行........
pool-1-thread-12正在执行........
pool-1-thread-17正在执行........
pool-1-thread-15正在执行........
newCachedThreadPool
内部调用的方法:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
可缓存的线程池:如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制
官方建议程序员使用较为方便的Executors
工厂方法Executors.newCachedThreadPool
()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)
(固定大小线程池)Executors.newSingleThreadExecutor()
(单个后台线程),这几种线程池均为大多数使用场景预定义了默认配置。
继承ThreadPoolExecutor类,并复写父类的构造方法
ThreadPoolExecutor
的构造方法
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;
}
前面的Executors
工厂类中创建线程池的几个工厂方法内部都是调用了此构造方法
先看看这个构造方法的参数的含义:
corePoolSize--池中所保存的线程数,包括空闲线程。
maximumPoolSize--池中允许的最大线程数。
keepAliveTime--当线程数大于corePoolSize时,此为终止空闲线程等待新任务的最长时间。
Unit--keepAliveTime 参数的时间单位。
workQueue--执行前用于保持任务的队列。此队列仅保持由 execute方法提交的 Runnable任务。
threadFactory--执行程序创建新线程时使用的工厂。
Handler--由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。
这几个参数之间的关系
接下来,咋们来说下这几个参数之间的关系。当线程池刚创建的时候,线程池里面是没有任何线程的(注意,并不是线程池一创建,里面就创建了一定数量的线程),当调用execute()方法添加一个任务时,线程池会做如下的判断:
- 如果当前正在运行的线程数量小于corePoolSize,那么立刻创建一个新的线程,执行这个任务。
- 如果当前正在运行的线程数量大于或等于corePoolSize,那么这个任务将会放入队列中。
- 如果线程池的队列已经满了,但是正在运行的线程数量小于maximumPoolSize,那么还是会创建新的线程,执行这个任务。
- 如果队列已经满了,且当前正在运行的线程数量大于或等于maximumPoolSize,那么线程池会根据拒绝执行策略来处理当前的任务。
- 当一个任务执行完后,线程会从队列中取下一个任务来执行,如果队列中没有需要执行的任务,那么这个线程就会处于空闲状态,如果超过了keepAliveTime存活时间,则这个线程会被线程池回收(注:回收线程是有条件的,如果当前运行的线程数量大于corePoolSize的话,这个线程就会被销毁,如果不大于corePoolSize,是不会销毁这个线程的,线程的数量必须保持在corePoolSize数量内).为什么不是线程一空闲就回收,而是需要等到超过keepAliveTime才进行线程的回收了,原因很简单:因为线程的创建和销毁消耗很大,更不能频繁的进行创建和销毁,当超过keepAliveTime后,发现确实用不到这个线程了,才会进行销毁。这其中unit表示keepAliveTime的时间单位,unit的定义如下:
线程池队列BlockingQueue
类结构图
SynchronousQueue
该队列对应的就是上面所说的直接提交,首先SynchronousQueue是无界的,也就是说他存数任务的能力是没有限制的,但是由于该Queue本身的特性,在某次添加元素后必须等待其他线程取走后才能继续添加
LinkedBlockingQueue
有界队列
构造方法
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
/**
* Creates a {@code LinkedBlockingQueue} with the given (fixed) capacity.
*
* @param capacity the capacity of this queue
* @throws IllegalArgumentException if {@code capacity} is not greater
* than zero
*/
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
/**
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}, initially containing the elements of the
* given collection,
* added in traversal order of the collection's iterator.
*
* @param c the collection of elements to initially contain
* @throws NullPointerException if the specified collection or any
* of its elements are null
*/
public LinkedBlockingQueue(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
final ReentrantLock putLock = this.putLock;
putLock.lock(); // Never contended, but necessary for visibility
try {
int n = 0;
for (E e : c) {
if (e == null)
throw new NullPointerException();
if (n == capacity)
throw new IllegalStateException("Queue full");
enqueue(new Node<E>(e));
++n;
}
count.set(n);
} finally {
putLock.unlock();
}
}
ArrayBlockingQueue
有界队列
构造方法
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
/**
* Creates an {@code ArrayBlockingQueue} with the given (fixed)
* capacity and the specified access policy.
*
* @param capacity the capacity of this queue
* @param fair if {@code true} then queue accesses for threads blocked
* on insertion or removal, are processed in FIFO order;
* if {@code false} the access order is unspecified.
* @throws IllegalArgumentException if {@code capacity < 1}
*/
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
public ArrayBlockingQueue(int capacity, boolean fair,
Collection<? extends E> c) {
this(capacity, fair);
final ReentrantLock lock = this.lock;
lock.lock(); // Lock only for visibility, not mutual exclusion
try {
int i = 0;
try {
for (E e : c) {
checkNotNull(e);
items[i++] = e;
}
} catch (ArrayIndexOutOfBoundsException ex) {
throw new IllegalArgumentException();
}
count = i;
putIndex = (i == capacity) ? 0 : i;
} finally {
lock.unlock();
}
}
fair
表示队列获取线程的策略是FIFO还是无序
线程池的拒绝执行策略
当线程的数量达到最大值时,这个时候,任务还在不断的来,这个时候,就只好拒绝接受任务了
ThreadPoolExecutor
允许自定义当添加任务失败后的执行策略。你可以调用线程池的 setRejectedExecutionHandler()
方法,用自定义的RejectedExecutionHandler
对象替换现有的策略ThreadPoolExecutor
提供的默认的处理策略是直接丢弃,同时抛异常信息,ThreadPoolExecutor
提供 4 个现有的策略,分别是:
ThreadPoolExecutor.AbortPolicy:表示拒绝任务并抛出异常
ThreadPoolExecutor.DiscardPolicy:表示拒绝任务但不做任何动作
ThreadPoolExecutor.CallerRunsPolicy:表示拒绝任务,并在调用者的线程中直接执行该任务
ThreadPoolExecutor.DiscardOldestPolicy:表示先丢弃任务队列中的第一个任务,然后把这个任务加进队列
JDK线程池的更多相关文章
- jdk线程池主要原理
本文转自:http://blog.csdn.net/linchengzhi/article/details/7567397 正常创建一个线程的时候,我们是这样的:new thread(Runnable ...
- JDK线程池和Spring线程池的使用
JDK线程池和Spring线程池实例,异步调用,可以直接使用 (1)JDK线程池的使用,此处采用单例的方式提供,见示例: public class ThreadPoolUtil { private s ...
- JDK 线程池
JDK 线程池 线程池参数 在JDK的4种线程池之前, 先介绍一下线程池的几个参数 corePoolSize 线程池的核心线程数量, maximumPoolSize 线程池的最大线程数量 keepAl ...
- 自己动手写线程池——向JDK线程池进发
自己动手写线程池--向JDK线程池进发 前言 在前面的文章自己动手写乞丐版线程池中,我们写了一个非常简单的线程池实现,这个只是一个非常简单的实现,在本篇文章当中我们将要实现一个和JDK内部实现的线程池 ...
- jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一)
jdk线程池ThreadPoolExecutor工作原理解析(自己动手实现线程池)(一) 线程池介绍 在日常开发中经常会遇到需要使用其它线程将大量任务异步处理的场景(异步化以及提升系统的吞吐量),而在 ...
- JDK线程池的拒绝策略
关于新疆服务请求未带入来话原因的问题 经核查,该问题是由于立单接口内部没有成功调用接续的 “更新来电原因接口”导致的,接续测更新来电原因接口编码:NGCCT_UPDATESRFLAG_PUT ,立单接 ...
- JDK线程池的使用
转载自:https://my.oschina.net/hosee/blog/614319: 摘要: 本系列基于炼数成金课程,为了更好的学习,做了系列的记录. 本文主要介绍: 1. 线程池的基本使用 2 ...
- juc线程池原理(六):jdk线程池中的设计模式
一.jdk中默认线程池中的代理模式 单例类线程池只有一个线程,无边界队列,适合cpu密集的运算.jdk中创建线程池是通过Executors类中提供的静态的方法来创建的,其中的单例类线程池的方法如下: ...
- JDK线程池的实现
线程池 接口Executor 该接口只有一个方法,JDK解释如下 执行已提交的Runnable 任务的对象.此接口提供一种将任务提交与每个任务将如何运行的机制(包括线程使用的细节.调度等)分离开来的方 ...
- jdk线程池,使用手记
Executors----------------------------------------------Executors------------------------------------ ...
随机推荐
- golang strings.Split函数
golang strings.Split函数 https://play.studygolang.com/ package main import ( "fmt" "str ...
- Ant <Delete> 如何只删掉文件夹下所有文件和文件夹
用 fileset 来过滤要删掉的目录和文件 <project name="ant-project" default="example"> < ...
- 曾经大量使用的Model1开发模式,虽不常用,但可以帮我们理解JSP
注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6513394762370777604/ 1.<JSP页面实际上就是Servlet> 2.<JSP页 ...
- Angularjs实现下拉列表排序
<select class="form-control underline" ng-model="reportform.score" ng-options ...
- Flowable实战(一)启动第一个完整流程
一.前言: 发现网上关于Flowable的资料基本都是浅尝辄止,对如何构建一个企业级的流程应用说明很少,所以写个实战系列,希望对大家和自己,都有所帮助. 二.认识Flowable Flowab ...
- Solon 1.6.15 发布,增加部分jdk17特性支持
关于官网 千呼万唤始出来: https://solon.noear.org .整了一个月多了...还得不断接着整! 关于 Solon Solon 是一个轻量级应用开发框架.支持 Web.Data.Jo ...
- 【刷题-LeetCode】264. Ugly Number II
Ugly Number II Write a program to find the n-th ugly number. Ugly numbers are positive numbers whose ...
- 【分享代码】bash中对一个逗号分隔的列表去重
直接上代码: #!/bin/bash dedup_list(){ v_list=$(echo "$1" | sed "s/,/\n/g" | sort -u | ...
- DEEP LEARNING WITH PYTORCH: A 60 MINUTE BLITZ | TORCH.AUTOGRAD
torch.autograd 是PyTorch的自动微分引擎,用以推动神经网络训练.在本节,你将会对autograd如何帮助神经网络训练的概念有所理解. 背景 神经网络(NNs)是在输入数据上执行的嵌 ...
- c++17 新特性
编译环境说明:gcc 8.1 + eclipse +windows 10 eclipse cpp默认支持c++14,做c++17开发时,需要手动进行配置. 1.关键字 1)constexpr c++1 ...