【JUC】6.线程池—ThreadPoolExecutor
创建线程池可以分为三种方式:
1. 通过ThreadPoolExecutor的构造方法,创建ThreadPoolExecutor的对象,即一个线程池对象;
此构造方法,一共7个参数,5个必须参数,2个带有默认值的参数;详细后面说;
传送:https://www.cnblogs.com/mussessein/p/11654022.html
2. 通过Executors返回的线程池对象;
这种方法创建的常用线程池为4种,还可以创建ForkJoinPool对象;
可以说是封装好的方法,通过Executors的4种常用静态方法,返回4种已经封装好的ThreadPoolExecutor线程池对象;
传送:https://www.cnblogs.com/mussessein/p/11654120.html
3. ForkJoinPool并发框架
将一个大任务拆分成多个小任务后,使用fork可以将小任务分发给其他线程同时处理,使用join可以将多个线程处理的结果进行汇总;这实际上就是分治思想。
为什么要使用线程池:
使用线程池的好处
降低资源消耗。重复利用已创建线程,降低线程创建与销毁的资源消耗。
提高响应效率。任务到达时,不需等待创建线程就能立即执行。
提高线程可管理性。
防止服务器过载。内存溢出、CPU耗尽。
进入正题:
ThreadPoolExecutor
尽量使用此类创建线程池,而非Executors创建;使用此方法,更明确线程池的运行规则,规避资源耗尽的风险
先说一下线程池的流程:
线程先进入核心池运行;
核心池满了,进队列等待;
队列满了,就创建新线程,直到最大线程数满了,之外的线程就被拒绝rejected;
在最后,会有代码演示,整个线程池的流程,很详细!
看构造器:
- // 七参构造器,前五个参数必须
- public ThreadPoolExecutor(int corePoolSize,
- int maximumPoolSize,
- long keepAliveTime,
- TimeUnit unit,
- BlockingQueue<Runnable> workQueue,
- ThreadFactory threadFactory, //可以不写
- RejectedExecutionHandler handler) // 可以不写
ThreadPoolExecutor的四种构造器的各项参数:
corePoolSize:核心池的大小,并非线程的最大数量
maximumPoolSize > corePoolSize
在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中
maximumPoolSize:线程池的最大线程数,表示线程池中最多能创建多少个线程
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止
默认:只有线程池内线程数大于corePoolSize的线程,keepAliveTime才会对其计时
当一个线程的空闲时间大于keepAliveTime,则会被终止
如果调用了allowCoreThreadTimeOut(boolean),线程池内线程数小于corePoolSize,keepAliveTime也会起作用
unit:参数keepAliveTime的时间单位(七种单位)
- TimeUnit.DAYS; //天
- TimeUnit.HOURS; //小时
- TimeUnit.MINUTES; //分钟
- TimeUnit.SECONDS; //秒
- TimeUnit.MILLISECONDS; //毫秒
- TimeUnit.MICROSECONDS; //微妙
- TimeUnit.NANOSECONDS; //纳秒
- TimeUnit.DAYS; //天
workQueue:选择一个阻塞队列
- LinkedBlockingQueue; // 常用,无界阻塞队列,不传值默认为Integer.MAX_VALUE,容易内存耗尽
- SynchronousQueue;
- ArrayBlockingQueue;
- PriorityBlockingQueue // 优先队列
- LinkedBlockingQueue; // 常用,无界阻塞队列,不传值默认为Integer.MAX_VALUE,容易内存耗尽
threadFactory:线程工厂,主要用来创建线程。如果不传此参数,默认:Executors.defaultThreadFactory()
RejectedExecutionHandler handler:表示当拒绝处理任务时的策略,有以下四种取值:
如果不传此参数,默认:ThreadPoolExecutor.AbortPolicy
- // 丢弃任务并抛出RejectedExecutionException异常。
- ThreadPoolExecutor.AbortPolicy
- // 也是丢弃任务,但是不抛出异常。
- ThreadPoolExecutor.DiscardPolicy
- // 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
- ThreadPoolExecutor.DiscardOldestPolicy
- // 由调用线程处理该任务
- ThreadPoolExecutor.CallerRunsPolicy
- // 丢弃任务并抛出RejectedExecutionException异常。
可以自行实现implements RejectedExecutionHandler接口,来自定义线程被线程池拒绝之后的操作:后面有演示;
ThreadPoolExecutor的重要方法:
execute(Runnable command)
通过这个方法可以向线程池提交一个任务,交由线程池去执行
此方法在执行的时候,会判断当前线程数是否大于corePoolSize
如果当前线程数大于corePoolSize,并且,当前线程池处于RUNNING状态,则将此任务加入任务缓冲队列
submit()
内部调用execute()方法
这个方法也是用来向线程池提交任务的,但是它和execute()方法不同
它能够返回任务执行的结果,利用了Future来获取任务执行结果
shutdown()
关闭线程池,此时线程池不能够接受新的任务,它会等待所有任务执行完毕
shutdownNow()
关闭线程池,线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务
演示线程池流程:
- public class Task implements Runnable {
- private String name;
- public Task(String name) {
- this.name = name;
- }
- @Override
- public void run() {
- try {
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- @Override
- public String toString() {
- return this.name;
- }
- }
- /**
- * 重写RejectedExecutionHandler
- * 自定义线程池拒绝线程之后的行为
- */
- public class MyRejectedHandler implements RejectedExecutionHandler {
- @Override
- public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
- System.out.println("Rejected:"+r.toString());
- }
- }
- /**
- * ThreadPoolExecutor的使用实例。
- * 四个必要参数,
- * RejectedHandler为自己定义的RejectedExecutionHandler实现类
- * 线程流程:
- * 1.进入核心池,核心池满了,之后线程,进入队列
- * 2.队列满了,继续创建线程,直到最大线程数
- * 3.最大线程数已满,拒绝后续线程
- */
- public class ThreadPoolDemo {
- public static void main(String[] args) {
- /**
- * 设置线程池参数:
- * 核心线程:2
- * 最大线程:3
- * 阻塞队列大小:5
- * 拒绝策略:自定义
- */
- ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
- 2, 3, 2000,
- TimeUnit.MILLISECONDS, new ArrayBlockingQueue(5), new MyRejectedHandler());
- // 启动一个主线程,内部启动10个子线程添加进线程池
- Runnable runTask = () -> {
- for (int i = 0; i < 10; i++) {
- String name = "Task_" + i;
- Task task = new Task(name);
- try {
- /**
- * 每次添加线程到线程池,都打印线程池的内部情况
- */
- threadPoolExecutor.execute(task);
- System.out.println("PoolSize: " + threadPoolExecutor.getPoolSize() +
- ",Queue" + threadPoolExecutor.getQueue());
- System.out.println();
- } catch (Exception e) {
- System.out.println("Refused:" + name);
- }
- }
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- };
- Thread thread = new Thread(runTask);
- thread.start();
- }
- }
运行结果:
- PoolSize: 1,Queue[]
- PoolSize: 2,Queue[]
- // 到这里,核心池满了,之后线程,进入队列
- PoolSize: 2,Queue[Task_2]
- PoolSize: 2,Queue[Task_2, Task_3]
- PoolSize: 2,Queue[Task_2, Task_3, Task_4]
- PoolSize: 2,Queue[Task_2, Task_3, Task_4, Task_5]
- PoolSize: 2,Queue[Task_2, Task_3, Task_4, Task_5, Task_6]
- // 队列满了,继续创建线程到线程池,这一个多余的线程会在等待,并倒计时keepAliveTime
- PoolSize: 3,Queue[Task_2, Task_3, Task_4, Task_5, Task_6]
- // 最大线程数已满,拒绝后续线程
- Rejected:Task_8
- PoolSize: 3,Queue[Task_2, Task_3, Task_4, Task_5, Task_6]
- Rejected:Task_9
- PoolSize: 3,Queue[Task_2, Task_3, Task_4, Task_5, Task_6]
执行execute()方法和submit()方法的区别
(1)execute() 方法用于提交不需要返回值的任务,所以无法判断任务是否被线程池执行成功与否;
(2)submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,而使用 get(long timeout,TimeUnit unit)
方法则会阻塞当前线程一段时间后立即返回,这时候有可能任务没有执行完。
【JUC】6.线程池—ThreadPoolExecutor的更多相关文章
- 硬核干货:4W字从源码上分析JUC线程池ThreadPoolExecutor的实现原理
前提 很早之前就打算看一次JUC线程池ThreadPoolExecutor的源码实现,由于近段时间比较忙,一直没有时间整理出源码分析的文章.之前在分析扩展线程池实现可回调的Future时候曾经提到并发 ...
- java面试总躲不过的并发(一): 线程池ThreadPoolExecutor基础梳理
本文核心:线程池ThreadPoolExecutor基础梳理 一.实现多线程的方式 1.继承Thread类,重写其run方法 2.实现Runnable接口,实现run方法 3.实现Callable接口 ...
- Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理
相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...
- Java线程池ThreadPoolExecutor使用和分析(一)
相关文章目录: Java线程池ThreadPoolExecutor使用和分析(一) Java线程池ThreadPoolExecutor使用和分析(二) - execute()原理 Java线程池Thr ...
- 线程池 ThreadPoolExecutor 原理及源码笔记
前言 前面在学习 JUC 源码时,很多代码举例中都使用了线程池 ThreadPoolExecutor,并且在工作中也经常用到线程池,所以现在就一步一步看看,线程池的源码,了解其背后的核心原理. 公众号 ...
- 细说JUC的线程池架构
前言 线程的创建是需要JVM和OS(操作系统)相互配合的,一次的创建要花费许多的资源. 1.首先,JVM要为该线程分配堆栈和初始化大量内存块,栈内存至少是1MB. 2.其次便是要进行系统的调用,在OS ...
- java线程池ThreadPoolExecutor使用简介
一.简介线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为:ThreadPoolExecutor(int corePoolSize, int m ...
- 线程池ThreadPoolExecutor
线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为: ThreadPoolExecutor(int corePoolSize, int maxi ...
- 关于线程池ThreadPoolExecutor使用总结
本文引用自: http://blog.chinaunix.net/uid-20577907-id-3519578.html 一.简介 线程池类为 java.util.concurrent.Thread ...
- [转] 引用 Java自带的线程池ThreadPoolExecutor详细介绍说明和实例应用
PS: Spring ThreadPoolTaskExecutor vs Java Executorservice cachedthreadpool 引用 [轰隆隆] 的 Java自带的线程池Thre ...
随机推荐
- 子页面赋值给父页面:window.opener.document.getElementById
window.opener 返回的是创建当前窗口的那个父窗口的引用,比如点击了a.htm上的一个链接而打开了b.htm,然后我们打算在b.htm上输入一个值然后赋予a.htm上的一个id为“name” ...
- Java3d 案例程序
今天偶尔翻出了很久以前写的java3d程序,很怀念曾经探索java3d解析.渲染ifc数据的日子 package com.vfsd.test0621; import java.applet.Apple ...
- Shell流程控制语句for
for语法格式: for 变量 in 参数列表 do 命令 done 或者 for 变量 in 参数列表 ; do 命令 done for语句流程控制图: 实例: [root@youxi1 ~]# v ...
- 深入分析GCC
深入分析GCC 目录 前言章 GCC概述 11.1 GCC的产生与发展 11.2 GCC的特点 21.3 GCC代码分析 3第2章 GCC源代码分析工具 42.1 vim ctags代码阅读工具 42 ...
- [LeetCode] 531. Lonely Pixel I 孤独的像素 I
Given a picture consisting of black and white pixels, find the number of black lonely pixels. The pi ...
- VS2013+OpenCV3.4.2编译
一.准备工作: (1)在OpenCV官网下载3.4.2版本(注意选择Win pack),https://opencv.org/releases.html. (2)下载Contrib模块,https:/ ...
- NGINX安全配置和限制访问
说起网络攻击,可能很多人只知道大名鼎鼎的DDOS攻击,这种攻击廉价且效果出众,直接通过第四层网络协议用他的带宽把你的带宽顶掉,造成网路阻塞,防不胜防,就连腾讯这种大鳄公司也被大流量DDOS搞过焦头烂额 ...
- spring AOP注解实现
一.什么是AOP 引用一下维基百科的定义 面向切面的程序设计(Aspect-oriented programming,AOP,又译作面向方面的程序设计.剖面导向程序设计)是计算机科学中的一种程序设计思 ...
- Qt编译理解(Qt 对 C++ 的扩展主要是3个方面)
沉沉的黑夜都是白天的前奏.--郭小川 舍弃IDE或qmake.cmake等工具的束缚,通过几个例子, 一步步从标准 C++ 的编译过渡到 Qt 的编译. Qt 对 C++ 的扩展主要是3个方面: 1) ...
- 为什么k8s引入pod概念?
为什么k8s引入pod概念? 1.可管理性 有些容器天生需要紧密关联,以pod为最小单位进行调度 扩展 共享资源 管理生命周期 例如: 一个容器写日志,一个容器读取日志进行相关内容的展示 2.通信和资 ...