Java线程池详细介绍与使用
文章目录
- 前言
- 一、线程池基础
- 1、什么是线程池
- 2、为什么使用线程池
- 3、线程池有那些优势
- 二、线程池使用
- 1、Java内置线程池:ThreadPoolExecutor
- 2、通过Executor工厂类中的静态方法获取线程池对象
- 第一种、通过newCachedThreadPool获取线程池对象
- 第二种、通过newFixedThreadPool获取线程池对象
- 第三种、通过newSingleThreadExecutor获取线程池对象
- 关闭线程方法:shutdown和shutdownNow的区别
- 三种创建线程池的区别
- 3、Java内置线程池-子接口:ScheduledExecutorService
- 方法一:通过schedule()延迟执行任务
- 方法二:通过scheduleAtFixedRate()方法延迟执行任务
- 方法三:通过scheduleWithFixedDelay()方法延迟执行任务
- ScheduleExecutorService中三个方法的小结
- 4、异步计算结果(Future)
- 三、线程池综合案例
- PS
前言
该篇关于多线程中的线程池是我在上篇Java基础没有写到的,当时想把整个多线程直接放在JavaSE中去,但发现网上很多讲Java课程的视频中讲多线程都没有讲到线程池,有些可能会说到,但比较少,可能只会介绍一种如何创建线程池的方式和使用,并没有详细说线程池,所以还是决定单独写一篇关于线程池的就好。而关于多线程的基础知识可以看我之前写的一篇文章,里面不仅有多线程的详细介绍和使用,还有很多的Java基础知识:JavaEE超详细介绍。最后会有一个关于多线程的综合案例,感兴趣的小伙伴有时间可以练一下
一、线程池基础
1、什么是线程池
用一句话来概述就是:线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后再需要执行新的任务时重用这些线程而不是新建线程。
2、为什么使用线程池
使用线程池最大的原因就是可以根据系统的需求和硬件环境灵活的控制线程的数量,且可以对所有线程进行统一的管理和控制,从而提高系统的运行效率,降低系统的运行压力。
3、线程池有那些优势
- 降低资源消耗:线程和任务分离,提高线程重用性
- 控制线程并发数量,降低服务器压力,统一管理所有线程
- 提高系统响应速度。假如创建线程用的时间为T1,执行任务的时间为T2,销毁线程的时间为T3,那么使用线程池就免去了T1和T3的时间。
二、线程池使用
1、Java内置线程池:ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler)
- corePoolSize:线程池核心线程数量
- maximumPoolSize:线程池最大线程数量
- keepAliverTime:当活跃线程数大于核心线程数时,空闲的多余线程最大存活时间
- unit:存活时间的单位
- workQueue:存放任务的队列
- handler:超出线程范围和队列容量的任务的处理程序
2、通过Executor工厂类中的静态方法获取线程池对象
通过Executor工厂类获取线程池有三种方式,这三种方式都是通过Executors类中的静态方法来获取的。如下:
第一种、通过newCachedThreadPool获取线程池对象
该方式特点是:创建一个默认的线程池对象,里面的线程可重用,且在第一次使用时才创建
package com.itheima.demo02;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;/*** 练习ExecuTors获取ExecutorService,然后调用方法,提交任务** @author Eric* @create 2021-10-01 13:33*/
public class MyTest1 {public static void main(String[] args) {//test1();test2();}//练习newCachedThreadPool方法private static void test1() {//1.使用工厂类获取线程池对象ExecutorService es = Executors.newCachedThreadPool();//2.提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable(i));}}//练习newCachedThreadPool(ThreadFactory threadFactory)方法private static void test2() {//1.使用工厂类获取线程池对象ExecutorService es = Executors.newCachedThreadPool(new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义的线程名称" + n++);}});//2.提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable(i));}}
}/*** 任务类,包含一个任务编号,在任务中打印出是那一个线程正在执行任务*/
class MyRunnable implements Runnable{private int id;public MyRunnable(int id) {this.id = id;}@Overridepublic void run() {//获取线程的名称,打印一句话String name = Thread.currentThread().getName();System.out.println(name + "执行了任务" + id);}
}
第二种、通过newFixedThreadPool获取线程池对象
该方式特点是:可指定创建线程数,并且可以重复用
package com.itheima.demo02;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;/*** 练习ExecuTors获取ExecutorService,然后调用方法,提交任务** @author Eric* @create 2021-10-01 13:33*/
public class MyTest2 {public static void main(String[] args) {//test1();test2();}//练习newFixedThreadPool方法private static void test1() {//1.使用工厂类获取线程池对象ExecutorService es = Executors.newFixedThreadPool(3);//2.提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable2(i));}}private static void test2() {//1.使用工厂类获取线程池对象ExecutorService es = Executors.newFixedThreadPool(3,new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义的线程名称" + n++);}});//2.提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable2(i));}}
}/*** 任务类,包含一个任务编号,在任务中打印出是那一个线程正在执行任务*/
class MyRunnable2 implements Runnable{private int id;public MyRunnable2(int id) {this.id = id;}@Overridepublic void run() {//获取线程的名称,打印一句话String name = Thread.currentThread().getName();System.out.println(name + "执行了任务" + id);}
}
第三种、通过newSingleThreadExecutor获取线程池对象
该方式特点是:只会创建一个线程
package com.itheima.demo02;import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;/*** 练习ExecuTors获取ExecutorService,然后调用方法,提交任务** @author Eric* @create 2021-10-01 13:33*/
public class MyTest3 {public static void main(String[] args) {//test1();test2();}//练习newSingleThreadExecutor方法private static void test1() {//1.使用工厂类获取线程池对象ExecutorService es = Executors.newSingleThreadExecutor();//2.提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable3(i));}}private static void test2() {//1.使用工厂类获取线程池对象ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义的线程名称" + n++);}});//2.提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable3(i));}}
}/*** 任务类,包含一个任务编号,在任务中打印出是那一个线程正在执行任务*/
class MyRunnable3 implements Runnable{private int id;public MyRunnable3(int id) {this.id = id;}@Overridepublic void run() {//获取线程的名称,打印一句话String name = Thread.currentThread().getName();System.out.println(name + "执行了任务" + id);}
}
关闭线程方法:shutdown和shutdownNow的区别
- shutdown():仅仅是不再接受新的任务,以前的任务还会继续执行
- shutdownNow():立刻关闭线程池,如果线程池中还有缓存的任务没有执行,则取消执行,并返回这些任务
具体代码如下:
package com.itheima.demo02;import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;/*** 练习ExecuTors获取ExecutorService,然后调用方法,提交任务** @author Eric* @create 2021-10-01 13:33*/
public class MyTest4 {public static void main(String[] args) {//test1();test2();}//练习newSingleThreadExecutor方法private static void test1() {//1.使用工厂类获取线程池对象ExecutorService es = Executors.newSingleThreadExecutor();//2.提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable4(i));}//3.关闭线程池,仅仅是不再接受新的任务,以前的任务还会继续执行es.shutdown();//演示:看能不能继续接收新任务//es.submit(new MyRunnable4(888));//不能再提交新的任务了,不然报异常}private static void test2() {//1.使用工厂类获取线程池对象ExecutorService es = Executors.newSingleThreadExecutor(new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义的线程名称" + n++);}});//2.提交任务for (int i = 1; i <= 10; i++) {es.submit(new MyRunnable4(i));}//3.立刻关闭线程池,如果线程池中还有缓存的任务没有执行,则取消执行,并返回这些任务List<Runnable> list = es.shutdownNow();System.out.println(list.toString());}
}/*** 任务类,包含一个任务编号,在任务中打印出是那一个线程正在执行任务*/
class MyRunnable4 implements Runnable{private int id;public MyRunnable4(int id) {this.id = id;}@Overridepublic void run() {//获取线程的名称,打印一句话String name = Thread.currentThread().getName();System.out.println(name + "执行了任务" + id);}@Overridepublic String toString() {return "MyRunnable4{" +"id=" + id +'}';}
}
三种创建线程池的区别
第一种:newCachedThreadPool:线程的数据是不做限制的,每次有任务来的时候都会以任务优先,性能最大化(也就是服务器压力比较大)
第二种:newFixedThreadPool:可以让压力不那么大,并且可以规定线程的数量,当线程的数量达到指定数量的时候,这个时候就不会再有新的线程了
第三种:newSingleThreadExecutor:绝对的安全,不考虑性能,因为是单线程,永远只有一个线程来执行任务。
3、Java内置线程池-子接口:ScheduledExecutorService
当你想控制线程池延迟执行或者重复执行,那么上面创建线程池的三种方式已经不能满足了,这个时候就需要用到我们的线程池的子接口:ScheduledExecutorService
ScheduledExecutorService介绍如下:
通过该子接口中方法来达到我们的需求:
方法一:通过schedule()延迟执行任务
具体代码如下:
package com.itheima.demo03;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;/*** 测试ScheduleExecutorService接口中的延迟执行任务和重复执行任务的功能** @author Eric* @create 2021-10-02 9:17*/
public class ScheduleExecutorServiceDemo01 {public static void main(String[] args) {//1.获取一个具备延迟执行任务的线程池对象ScheduledExecutorService es = Executors.newScheduledThreadPool(3);//2.创建多个任务对象,提交任务,每个任务延迟2秒执行es.schedule(new MyRunnable(1),2, TimeUnit.SECONDS);System.out.println("over");}
}class MyRunnable implements Runnable{private int id;public MyRunnable(int id) {this.id = id;}@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(name + "执行了任务:" + id);}
}
方法二:通过scheduleAtFixedRate()方法延迟执行任务
具体代码如下:
package com.itheima.demo03;import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;/*** 测试ScheduleExecutorService接口中的延迟执行任务和重复执行任务的功能** @author Eric* @create 2021-10-02 9:17*/
public class ScheduleExecutorServiceDemo02 {public static void main(String[] args) {//1.获取一个具备延迟执行任务的线程池对象ScheduledExecutorService es = Executors.newScheduledThreadPool(3, new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义线程名称:" + n++);}});//2.创建多个任务对象,提交任务,每个任务延迟2秒执行 //初始等待1秒,执行任务间隔2秒es.scheduleAtFixedRate(new MyRunnable2(1),1,2,TimeUnit.SECONDS);System.out.println("over");}
}class MyRunnable2 implements Runnable{private int id;public MyRunnable2(int id) {this.id = id;}@Overridepublic void run() {String name = Thread.currentThread().getName();try {Thread.sleep(1500);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + "执行了任务:" + id);}
}
方法三:通过scheduleWithFixedDelay()方法延迟执行任务
package com.itheima.demo03;import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;/*** 测试ScheduleExecutorService接口中的延迟执行任务和重复执行任务的功能** @author Eric* @create 2021-10-02 9:17*/
public class ScheduleExecutorServiceDemo03 {public static void main(String[] args) {//1.获取一个具备延迟执行任务的线程池对象ScheduledExecutorService es = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {int n = 1;@Overridepublic Thread newThread(Runnable r) {return new Thread(r,"自定义线程名称:" + n++);}});//2.创建多个任务对象,提交任务,每个任务延迟2秒执行 //初始延迟1秒,执行任务间隔2秒(任务执行时间不计入的,是等任务执行之后再间隔2秒)es.scheduleWithFixedDelay(new MyRunnable3(1),1,2,TimeUnit.SECONDS);System.out.println("over");}
}class MyRunnable3 implements Runnable{private int id;public MyRunnable3(int id) {this.id = id;}@Overridepublic void run() {String name = Thread.currentThread().getName();try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(name + "执行了任务:" + id);}
}
ScheduleExecutorService中三个方法的小结
1、设置延迟多长时间任务执行,可以使用schedule()方法
2、设置每间隔多长时间执行,有两种方式:
方式一:只计算第一次开始到第二次开始的间隔时间
方式二:第一次任务结束之后开始计时,间隔多长时间,到第二次任务开始之前,这个时间段。
4、异步计算结果(Future)
介绍如下:
具体操作代码如下:
package com.itheima.demo04;import java.util.concurrent.*;/*** 练习异步计算结果** @author Eric* @create 2021-10-02 10:30*/
public class FutureDemo {public static void main(String[] args) throws Exception {//1.获取线程池对象ExecutorService es = Executors.newCachedThreadPool();//2.创建Callable类型对象Future<Integer> f = es.submit(new MyCall(1, 1));//3.判断任务是否已经完成//test1(f);//测试get()方法//boolean b = f.cancel(true);//System.out.println("取消任务执行的结果:" + b);//Integer v = f.get(1, TimeUnit.SECONDS);//由于等待时间过短,任务来不及执行完成,所以会报异常//System.out.println("任务执行的结果是:" +v);}//正常执行流程private static void test1(Future<Integer> f) throws Exception {boolean done = f.isDone();System.out.println("第一次判断任务是否完成:" + done);boolean cancelled = f.isCancelled();System.out.println("第一次判断任务是否取消:" + cancelled);Integer v = f.get();//一直等待任务的执行,直到完成System.out.println("任务执行的结果是:" + v);//再一次判断任务是否完成boolean done2 = f.isDone();System.out.println("第二次判断任务是否完成:" + done2);boolean cancelled2 = f.isCancelled();System.out.println("第二次判断任务是否取消:" + cancelled2);}
}class MyCall implements Callable<Integer>{private int a;private int b;//通过构造方法传递两个参数public MyCall(int a, int b) {this.a = a;this.b = b;}@Overridepublic Integer call() throws Exception {String name = Thread.currentThread().getName();System.out.println(name + "准备开始计算...");Thread.sleep(2000);System.out.println(name + "计算完成...");return a + b;}
}
三、线程池综合案例
最后,拿一个综合案例来结合前面写的融合一下,感兴趣的小伙伴可以锻炼一下。
具体代码如下:
任务类代码:
package com.itheima.demo05;/*** 任务类:* 包含了商品数量,客户名称,送手机的行为** @author Eric* @create 2021-10-02 10:46*/
public class MyTask implements Runnable{//设计一个变量,用于表示商品的数量private static int id = 10;//表示客户名称变量private String userName;public MyTask(String userName) {this.userName = userName;}@Overridepublic void run() {String name = Thread.currentThread().getName();System.out.println(userName + "正在使用" + name + "参与秒杀任务...");try {Thread.sleep(200);//为了逼真一点,休眠200毫秒} catch (InterruptedException e) {e.printStackTrace();}synchronized (MyTask.class){if(id > 0){System.out.println(userName + "使用" + name + "秒杀:" + id-- + "号商品成功啦!");}else {System.out.println(userName + "使用" + name + "秒杀失败了!");}}}
}
测试类代码:
package com.itheima.demo05;import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;/*** 主程序类,测试任务类** @author Eric* @create 2021-10-02 10:50*/
public class MyTest {public static void main(String[] args) {//1.创建一个线程池对象ThreadPoolExecutor pool = new ThreadPoolExecutor(3,5,1, TimeUnit.MINUTES,new LinkedBlockingQueue<>(15));//2.循环创建任务对象for (int i = 0; i < 20; i++) {MyTask myTask = new MyTask("客户" + i);pool.submit(myTask);}//3.关闭线程池pool.shutdown();}
}
PS
在实际项目中,我们使用多线程的话都是通过JDK8新特性中的 CompletableFuture ,感兴趣的小伙伴可以查看这篇文章:多线程的复习和多线程的异步编排详解(CompletableFuture详细介绍和使用)
该文章也是为了加深一下自己的理解和影响,当然,如果对你也有帮助,是我的荣幸~ 如有不对的地方请各位指教,感谢~
Java线程池详细介绍与使用相关推荐
- Java线程池详细介绍——原理及详细使用
原文链接:https://www.toutiao.com/i6846340200134607374/ 关于线程和线程池的学习,我们可以从以下几个方面入手: 第一,什么是线程,线程和进程的区别是什么 第 ...
- Java 线程池的介绍以及工作原理
在什么情况下使用线程池? 1.单个任务处理的时间比较短 2.将需处理的任务的数量大 使用线程池的好处: 1. 降低资源消耗: 通过重复利用已创建的线程降低线程创建和销毁造成的消耗. 2. 提高响应速度 ...
- Java线程池的实际应用:一根木棍 随机分割三段 组成三角形的概率 多线程解决
java线程池的介绍全网很多,大家可以查询得到,我就不多赘述了(讲的可能还不如那些资料好) 来看一个实际问题: 我们有一根长度为1的木棍,现在我们随机将它分割成三份,那么这三段木棍能组成一个三角形的概 ...
- JAVA线程池原理以及几种线程池类型介绍
在什么情况下使用线程池? 1.单个任务处理的时间比较短 2.将需处理的任务的数量大 使用线程池的好处: 1.减少在创建和销毁线程上所花的时间以及系统资源的开销 2.如不使用线程池, ...
- java线程池有什么作用_java线程池的作用是什么?线程池介绍
你知道java中线程池的作用是什么吗?那么究竟什么是线程池呢?都有哪些类型呢?让我们对以上的问题来进行详细的了解吧. 一.java线程池作用 第一个我们先来对它的作用进行一下简单的介绍,使用线程池的优 ...
- Java线程池(超详细)
文章目录 1. 线程池概念 2. JUC线程池架构 3. Executors创建线程的4种方法 4. 线程池的标准创建方式 5. 向线程池提交任务的两种方式 6. 线程池的任务调度流程 7. Thre ...
- Java线程池使用与原理
线程池是什么? 我们可以利用java很容易创建一个新线程,同时操作系统创建一个线程也是一笔不小的开销.所以基于线程的复用,就提出了线程池的概念,我们使用线程池创建出若干个线程,执行完一个任务后,该线程 ...
- Java线程池实现原理及其在美团业务中的实践
来自:美团技术团队 随着计算机行业的飞速发展,摩尔定律逐渐失效,多核CPU成为主流.使用多线程并行计算逐渐成为开发人员提升服务器性能的基本武器.J.U.C提供的线程池ThreadPoolExecuto ...
- java线程池的使用例子,不愧是大佬
京东Java研发岗一面(基础面,约1小时) 自我介绍,主要讲讲做了什么和擅长什么 springmvc和spring-boot区别 @Autowired的实现原理 Bean的默认作用范围是什么?其他的作 ...
最新文章
- [转]程序员技术练级攻略
- java 与c 运行效率_Java语言与C语言代码运行效率的比较
- boost::multiprecision模块hash相关的测试程序
- 【数据结构与算法】之深入解析“有效的括号”的求解思路与算法示例
- VS2015升级Update2之后Cordova程序提示:此应用程序无法在此电脑上运行
- 二陈丸配什么吃不上火_宝妈一个人带孩子是什么感觉?前三种场景,不知道是怎么熬过来的...
- 9种对抗电脑辐射的方法
- 图书销售系统系统设计说明书
- ajax请求在ie浏览器上的兼容性问题
- 计算机 审计追踪功能,第 讲 审计追踪技术与Windows安全审计功能
- java.lang.NumberFormatException: null的解决方法
- 车内静谧性超越埃尔法?走进腾势D9身价上亿的NVH实验室
- 中国传统文化讲坛之“春风拂槛”唐文化论坛成功举办
- B2C电子商务网站使用Spring发送激活账号的电子邮件
- 记flume部署过程中遇到的问题以及解决方法(持续更新)
- 大学四年如何规划之出国留学
- 《炬丰科技-半导体工艺》化学蚀刻的铜-ETP铜
- 2020 继续踏踏实实的做好自己
- eNSP配置基于VRRP的负载均衡出口链路
- google常用语法
热门文章
- Ubuntu20.04设置中文失败或重启恢复英文的解决方案
- 同一账号不能多地登录(限制同一账号同一时刻只能一个用户登
- k2p一直亮红灯搜不到信号_#苹果商店藏下载骗局#,苹果APPStore“绝地求生13天”背后的信号(ASOer必看)...
- jedis和jedisPool
- 对Map的一些分析和总结,有序map,排序,
- python 中缩进的作用_缩进在Python中的作用有哪些?Python缩进案例分享
- svg可爱仙人掌卡通代码
- 6月22日の勉強レポート
- 开放源码!大学生用C语言自制网站服务器 只要100行代码!
- form表单序列化转换为json对象