使用ExecutorService实现线程池
ExecutorService是java提供的用于管理线程池的类。
线程池的作用:
- 控制线程数量
- 重用线程
当一个程序中创建了许多线程,并在任务结束后销毁,会给系统带来过度消耗资源,以及过度切换线程的危险,从而可能导致系统崩溃。为此我们应使用线程池来解决这个问题。
线程池的概念:
首先创建一些线程,它们的集合称为线程池,当服务器受到一个客户请求后,就从线程池中取出一个空闲的线程为之服务,服务完后不关闭该线程,而是将该线程还回到线程池中。
在线程池编程模式下,任务是提交给整个线程池,而不是交给某个线程,线程池拿到任务就在内部找空闲的线程,再把任务交给内部的空闲线程,一个线程只能执行一个任务,但可以向线程池提交多个任务。
线程池的主要实现方法:
1、 Executors.newFixedThreadPool(int nThreads);
说明:创建固定大小(nThreads, 大小不能超过int的最大值)的线程池
// 线程数量 int nThreads = 20;
// 创建executor 服务 : ExecutorService executor = Executors.newFixedThreadPool(nThreads) ;
重载后的版本,需要传入实现了ThreadFactory接口的对象。
ExecutorService executor = Executors. newFixedThreadPool(nThreads, threadFactory);
说明:创建固定大小(nThreads, 大小不能超过int的最大值) 的线程池,缓冲任务的队列为LinkedBlockingQueue,大小为整型的最大数,当使用此线程池时,在同执行的任务数量超过传入的线程池大小值后,将会放入LinkedBlockingQueue,在LinkedBlockingQueue中的任务需要等待线程空闲后再执行,如果放入LinkedBlockingQueue中的任务超过整型的最大数时,抛出RejectedExecutionException。
2、Executors.newSingleThreadExecutor():创建大小为1的固定线程池。
ExecutorService executor = Executors.newSingleThreadExecutor();
重载后的版本,需要多传入实现了ThreadFactory接口的对象。
ExecutorService executor = Executors. newSingleThreadScheduledExecutor(ThreadFactory threadFactory)
说明:创建大小为1的固定线程池,执行任务的线程只有一个,其它的(任务)task都放在LinkedBlockingQueue中排队等待执行。
3、Executors.newCachedThreadPool();创建corePoolSize为0,最大线程数为整型的最大数,线程 keepAliveTime为1分钟,缓存任务的队列为SynchronousQueue的线程池。
ExecutorService executor = Executors.newCachedThreadPool();
当然也可以以下面的方式创建,重载后的版本,需要多传入实现了ThreadFactory接口的对象。
ExecutorService executor = Executors.newCachedThreadPool(ThreadFactory threadFactory) ;
说明:使用时,放入线程池的task任务会复用线程或启动新线程来执行,注意事项:启动的线程数如果超过整型最大值后会抛出RejectedExecutionException异常,启动后的线程存活时间为一分钟。
4、Executors.newScheduledThreadPool(int corePoolSize):创建corePoolSize大小的线程池。
// 线程数量 int corePoolSize= 20;
// 创建executor 服务 : ExecutorService executor = Executors.newScheduledThreadPool(corePoolSize) ;
重载后的版本,需要多传入实现了ThreadFactory接口的对象。
ExecutorService executor = Executors.newScheduledThreadPool(corePoolSize, threadFactory) ;
说明:线程keepAliveTime为0,缓存任务的队列为DelayedWorkQueue,注意不要超过整型的最大值。
这种线程池有些不同,它可以实现定时器执行任务的功能,下面对第四种线程池进行代码演示:
- import java.util.concurrent.Executors;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.ScheduledExecutorService;
- import java.util.concurrent.ScheduledFuture;
- import java.util.concurrent.TimeUnit;
- class Temp extends Thread {
- public void run() {
- System.out.println("run");
- }
- }
- public class ScheduledJob {
- public static void main(String args[]) throws Exception {
- Temp command = new Temp();
- ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
- //command代表执行的任务,5代表延迟5秒后开始执行,1代表每隔1秒执行一次,TimeUnit.SECONDS代表时间单位是秒
- ScheduledFuture<?> scheduleTask = scheduler.scheduleWithFixedDelay(command, 5, 1, TimeUnit.SECONDS);
- }
- }
下面以一段代码演示一般情况线程池的使用:
- public static void main(String[] args) {
- ExecutorService threadpoo1 = Executors.newFixedThreadPool(2);
- for(int i=0;i<5;i++){
- Runnable runn=new Runnable() {
- public void run() {
- Thread t=Thread.currentThread();
- try {
- System.out.println(t+":正在运行");
- Thread.sleep(5000);
- System.out.println(t+"运行结束");
- } catch (Exception e) {
- System.out.println("线程被中断了");
- }
- }
- };
- threadpoo1.execute(runn);
- System.out.println("指派了一个任务交给线程池");
- threadpoo1.shutdown();
- System.out.println("停止线程池了!");
- }
自定义线程池
corePoolSize:核心池的大小,这个参数跟后面讲述的线程池的实现原理有非常大的关系。在创建了线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法,从这2个方法的名字就可以看出,是预创建线程的意思,即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中;
maximumPoolSize:线程池最大线程数,这个参数也是一个非常重要的参数,它表示在线程池中最多能创建多少个线程;
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
unit:参数keepAliveTime的时间单位
workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般来说,这里的阻塞队列有以下几种选择:
ArrayBlockingQueue;
LinkedBlockingQueue;
SynchronousQueue;
threadFactory:线程工厂,主要用来创建线程;
handler:表示当拒绝处理任务时的策略,默认有以下四种取值:
ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务
下面对自定义线程池进行代码演示:
MyTask实例类
- package com.bjsxt.height.concurrent018;
- public class MyTask implements Runnable {
- private int taskId;
- private String taskName;
- public MyTask(int taskId, String taskName){
- this.taskId = taskId;
- this.taskName = taskName;
- }
- public int getTaskId() {
- return taskId;
- }
- public void setTaskId(int taskId) {
- this.taskId = taskId;
- }
- public String getTaskName() {
- return taskName;
- }
- public void setTaskName(String taskName) {
- this.taskName = taskName;
- }
- @Override
- public void run() {
- try {
- System.out.println("run taskId =" + this.taskId);
- Thread.sleep(5*1000);
- //System.out.println("end taskId =" + this.taskId);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- public String toString(){
- return Integer.toString(this.taskId);
- }
- }
- package com.bjsxt.height.concurrent018;
- import java.util.concurrent.ArrayBlockingQueue;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.Executors;
- import java.util.concurrent.LinkedBlockingQueue;
- import java.util.concurrent.ThreadPoolExecutor;
- import java.util.concurrent.ThreadPoolExecutor.DiscardOldestPolicy;
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy;
- public class UseThreadPoolExecutor1 {
- public static void main(String[] args) {
- /**
- * 在使用有界队列时,若有新的任务需要执行,如果线程池实际线程数小于corePoolSize,则优先创建线程,
- * 若大于corePoolSize,则会将任务加入队列,
- * 若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建新的线程,
- * 若线程数大于maximumPoolSize,则执行拒绝策略。或其他自定义方式。
- *
- */
- ThreadPoolExecutor pool = new ThreadPoolExecutor(
- 1, //coreSize
- 2, //MaxSize
- 60, //
- TimeUnit.SECONDS,
- new ArrayBlockingQueue<Runnable>(3) //指定一种队列 (有界队列)
- //new LinkedBlockingQueue<Runnable>()
- , new MyRejected()
- //, new DiscardOldestPolicy()
- );
- MyTask mt1 = new MyTask(1, "任务1");
- MyTask mt2 = new MyTask(2, "任务2");
- MyTask mt3 = new MyTask(3, "任务3");
- MyTask mt4 = new MyTask(4, "任务4");
- MyTask mt5 = new MyTask(5, "任务5");
- MyTask mt6 = new MyTask(6, "任务6");
- pool.execute(mt1);
- pool.execute(mt2);
- pool.execute(mt3);
- pool.execute(mt4);
- pool.execute(mt5);
- pool.execute(mt6);
- pool.shutdown();
- }
- }
- package com.bjsxt.height.concurrent018;
- import java.util.concurrent.ArrayBlockingQueue;
- import java.util.concurrent.BlockingQueue;
- import java.util.concurrent.ExecutorService;
- import java.util.concurrent.LinkedBlockingQueue;
- import java.util.concurrent.ThreadPoolExecutor;
- import java.util.concurrent.TimeUnit;
- import java.util.concurrent.atomic.AtomicInteger;
- public class UseThreadPoolExecutor2 implements Runnable{
- private static AtomicInteger count = new AtomicInteger(0);
- @Override
- public void run() {
- try {
- int temp = count.incrementAndGet();
- System.out.println("任务" + temp);
- Thread.sleep(2000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- public static void main(String[] args) throws Exception{
- //System.out.println(Runtime.getRuntime().availableProcessors());
- BlockingQueue<Runnable> queue =
- //new LinkedBlockingQueue<Runnable>();
- new ArrayBlockingQueue<Runnable>(10);
- ExecutorService executor = new ThreadPoolExecutor(
- 5, //core
- 10, //max
- 120L, //2fenzhong
- TimeUnit.SECONDS,
- queue);
- for(int i = 0 ; i < 20; i++){
- executor.execute(new UseThreadPoolExecutor2());
- }
- Thread.sleep(1000);
- System.out.println("queue size:" + queue.size()); //
- Thread.sleep(2000);
- }
- }
自定义拒绝策略演示
- package com.bjsxt.height.concurrent018;
- import java.net.HttpURLConnection;
- import java.util.concurrent.RejectedExecutionHandler;
- import java.util.concurrent.ThreadPoolExecutor;
- public class MyRejected implements RejectedExecutionHandler{
- public MyRejected(){
- }
- @Override
- public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
- System.out.println("自定义处理..");
- System.out.println("当前被拒绝任务为:" + r.toString());
- }
- }
使用ExecutorService实现线程池的更多相关文章
- Java 并发工具包——ExecutorService常用线程池
1. 执行器服务 ExecutorService java.util.concurrent.ExecutorService 接口表示一个异步执行机制,使我们能够在后台执行任务.因此一个 Executo ...
- 转:java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例
java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例 1.CountDownLatch:一个同步工具类,它允许一个或多个线程一 ...
- Java基础学习笔记: 多线程,线程池,同步锁(Lock,synchronized )(Thread类,ExecutorService ,Future类)(卖火车票案例)
多线程介绍 学习多线程之前,我们先要了解几个关于多线程有关的概念.进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线 ...
- Android单例线程池
package com.jredu.schooltong.manager; import java.util.concurrent.ExecutorService;import java.util.c ...
- Java并发——线程池Executor框架
线程池 无限制的创建线程 若采用"为每个任务分配一个线程"的方式会存在一些缺陷,尤其是当需要创建大量线程时: 线程生命周期的开销非常高 资源消耗 稳定性 引入线程池 任务是一组逻辑 ...
- java异步线程池同时请求多个接口数据
一.主要使用类 . ExecutorService java线程池类 申明方式:ExecutorService exc = Executors.newFixedThreadPool(requestPa ...
- java线程池01-ThreadPoolExecutor构造方法参数的使用规则
为了更好的使用多线程,JDK提供了线程池供开发人员使用,目的在于减少线程的创建和销毁次数,以此达到线程的重复利用. 其中ThreadPoolExecutor是线程池中最核心的一个类,我们先简单看一下这 ...
- Java多线程、线程池和线程安全整理
多线程 1.1 多线程介绍 进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 1.2 Thread类 通 ...
- Java 基础 多线程和线程池基础
一,多线程 1.1 多线程介绍 进程:进程指正在运行的程序.确切的来说,当一个程序进入内存运行,即变成一个进程,进程是处于运行过程中的程序,并且具有一定独立功能. 线程:线程是进程中的一个执行单元,负 ...
随机推荐
- 转载:EJB到底是什么
这篇博客用通俗易懂的语言对EJB进行了介绍,写得很好,笔者在这里转载一下. 链接:https://www.cnblogs.com/strugglion/p/6027318.html
- 基于SpringBoot+SpringSecurity+mybatis+layui实现的一款权限系统
这是一款适合初学者学习权限以及springBoot开发,mybatis综合操作的后台权限管理系统 其中设计到的数据查询有一对一,一对多,多对多,联合分步查询,充分利用mybatis的强大实现各种操作, ...
- Java四舍五入时保留指定小数位数
方式一: double f = 3.1516; BigDecimal b = new BigDecimal(f); , BigDecimal.ROUND_HALF_UP).doubleValue(); ...
- CentOS7下PHP7.2安装redis扩展
1.安装phpize(存在忽略) yum install php-devel 2.下载扩展源码包,直接用wget,一般放在 /usr/local/src wget https://github.com ...
- js根据年份获取某月份有几天
function getNum(year, month) { var temp; month = parseInt(month, 10); temp = new Date(year, month, 0 ...
- vue 路由对象(常用的)
路由对象 在使用了 vue-router 的应用中,路由对象会被注入每个组件中,赋值为 this.$route ,并且当路由切换时,路由对象会被更新. 路由对象暴露了以下属性: $route.path ...
- JavaSE库存管理系统项目实战
需求分析 企业库房用于存放成品.半成品.原材料.工具等物资,一般情况下,这些物资统称为物料.库存管理常见业务包括物资的入库.出库.盘点.退货.报废以及财务核算等,业务逻辑比较复杂,库房信息系统项目开发 ...
- Python正则表达式-基础
Python正则表达式-基础 本文转载自昔日暖阳,原文地址:http://www.osheep.cn/4806.html python使用正则,需要先引入re模块 import re 匹配符 单个字符 ...
- Vue简单使用,
一些零碎的知识点: 在js中变量的声明 有三种方式: let,var, const let: 对应的是一个块级作用域 { let a = 12 } console.log(a) 这是未声明的, var ...
- CMDB介绍
CMDB https://lupython.gitee.io/2018/05/05/CMDB%E4%BB%8B%E7%BB%8D/ 尚泽凯博客地址 传统运维与自动化运维的区别 传统运维: 1.项目 ...