Java并发编程(08):Executor线程池框架

本文源码:GitHub·点这里 || GitEE·点这里

一、Executor框架简介

1、基础简介

Executor系统中,将线程任务提交和任务执行进行了解耦的设计,Executor有各种功能强大的实现类,提供便捷方式来提交任务并且获取任务执行结果,封装了任务执行的过程,不再需要Thread().start()方式,显式创建线程并关联执行任务。

2、调度模型

线程被一对一映射为服务所在操作系统线程,启动时会创建一个操作系统线程;当该线程终止时,这个操作系统线程也会被回收。

3、核心API结构

Executor框架包含的核心接口和主要的实现类如下图所示:

线程池任务:核心接口:Runnable、Callable接口和接口实现类;

任务的结果:接口Future和实现类FutureTask;

任务的执行:核心接口Executor和ExecutorService接口。在Executor框架中有两个核心类实现了ExecutorService接口,ThreadPoolExecutor和ScheduledThreadPoolExecutor。

二、用法案例

1、API基础

ThreadPoolExecutor基础构造

1public ThreadPoolExecutor(int corePoolSize, 2 int maximumPoolSize, 3 long keepAliveTime, 4 TimeUnit unit, 5 BlockingQueue<Runnable> workQueue, 6 ThreadFactory threadFactory, 7 RejectedExecutionHandler handler) {}

参数名

说明

corePoolSize

线程池的核心大小,队列没满时,线程最大并发数

maximumPoolSize

最大线程池大小,队列满后线程能够容忍的最大并发数

keepAliveTime

空闲线程等待回收的时间限制

unit

keepAliveTime时间单位

workQueue

阻塞的队列类型

threadFactory

创建线程的工厂,一般用默认即可

handler

超出工作队列和线程池时,任务会默认抛出异常

2、初始化方法

1ExecutorService :Executors.newFixedThreadPool(); 2ExecutorService :Executors.newSingleThreadExecutor(); 3ExecutorService :Executors.newCachedThreadPool(); 4 5ThreadPoolExecutornew ThreadPoolExecutor() ;

通常情况下,线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式更加明确线程池的运行规则,规避资源耗尽的风险。

3、基础案例

1package com.multy.thread.block08executor; 2import java.util.concurrent.*; 3 4public class Executor01 { 5 // 定义线程池 6 private static ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor( 7 3,10,5000,TimeUnit.SECONDS, 8 new SynchronousQueue<>(),Executors.defaultThreadFactory(),new ExeHandler()); 9 public static void main(String[] args) { 10 for (int i = 0 ; i < 100 ; i++){ 11 poolExecutor.execute(new PoolTask(i)); 12 //带返回值:poolExecutor.submit(new PoolTask(i)); 13 } 14 } 15} 16// 定义线程池任务 17class PoolTask implements Runnable { 18 19 private int numParam; 20 21 public PoolTask (int numParam) { 22 this.numParam = numParam; 23 } 24 @Override 25 public void run() { 26 try { 27 System.out.println("PoolTask "+ numParam+" begin..."); 28 Thread.sleep(5000); 29 } catch (Exception e) { 30 e.printStackTrace(); 31 } 32 } 33 public int getNumParam() { 34 return numParam; 35 } 36 public void setNumParam(int numParam) { 37 this.numParam = numParam; 38 } 39} 40// 定义异常处理 41class ExeHandler implements RejectedExecutionHandler { 42 @Override 43 public void rejectedExecution(Runnable runnable, ThreadPoolExecutor executor) { 44 System.out.println("ExeHandler "+executor.getCorePoolSize()); 45 executor.shutdown(); 46 } 47}

流程分析

  • 线程池中线程数小于corePoolSize时,新任务将创建一个新线程执行任务,不论此时线程池中存在空闲线程;
  • 线程池中线程数达到corePoolSize时,新任务将被放入workQueue中,等待线程池中任务调度执行;
  • 当workQueue已满,且maximumPoolSize>corePoolSize时,新任务会创建新线程执行任务;
  • 当workQueue已满,且提交任务数超过maximumPoolSize,任务由RejectedExecutionHandler处理;
  • 当线程池中线程数超过corePoolSize,且超过这部分的空闲时间达到keepAliveTime时,回收该线程;
  • 如果设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize范围内的线程空闲时间达到keepAliveTime也将回收;

三、线程池应用

应用场景:批量账户和密码的校验任务,在实际的业务中算比较常见的,通过初始化线程池,把任务提交执行,最后拿到处理结果,这就是线程池使用的核心思想:节省资源提升效率。

1public class Executor02 { 2 3 public static void main(String[] args) { 4 // 初始化校验任务 5 List<CheckTask> checkTaskList = new ArrayList<>() ; 6 initList(checkTaskList); 7 // 定义线程池 8 ExecutorService executorService ; 9 if (checkTaskList.size() < 10){ 10 executorService = Executors.newFixedThreadPool(checkTaskList.size()); 11 }else{ 12 executorService = Executors.newFixedThreadPool(10); 13 } 14 // 批量处理 15 List<Future<Boolean>> results = new ArrayList<>() ; 16 try { 17 results = executorService.invokeAll(checkTaskList); 18 } catch (InterruptedException e) { 19 Thread.currentThread().interrupt(); 20 } 21 // 查看结果 22 for (Future<Boolean> result : results){ 23 try { 24 System.out.println(result.get()); 25 // System.out.println(result.get(10000,TimeUnit.SECONDS)); 26 } catch (Exception e) { 27 e.printStackTrace() ; 28 } 29 } 30 // 关闭线程池 31 executorService.shutdownNow(); 32 } 33 34 private static void initList (List<CheckTask> checkTaskList){ 35 checkTaskList.add(new CheckTask("root","123")) ; 36 checkTaskList.add(new CheckTask("root1","1234")) ; 37 checkTaskList.add(new CheckTask("root2","1235")) ; 38 } 39} 40// 校验任务 41class CheckTask implements Callable<Boolean> { 42 private String userName ; 43 private String passWord ; 44 public CheckTask(String userName, String passWord) { 45 this.userName = userName; 46 this.passWord = passWord; 47 } 48 @Override 49 public Boolean call() throws Exception { 50 // 校验账户+密码 51 if (userName.equals("root") && passWord.equals("123")){ 52 return Boolean.TRUE ; 53 } 54 return Boolean.FALSE ; 55 } 56}

线程池主要用来解决线程生命周期开销问题和资源不足问题,通过线程池对多个任务线程重复使用,线程创建也被分摊到多个任务上,多数任务提交就有空闲的线程可以使用,所以消除线程频繁创建带来的开销。

四、源代码地址

1GitHub·地址 2https://github.com/cicadasmile/java-base-parent 3GitEE·地址 4https://gitee.com/cicadasmile/java-base-parent

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

Opencv中Mat矩阵相乘——点乘、dot、mul运算详解

Opencv中Mat矩阵相乘——点乘、dot、mul运算详解2016年09月02日00:00:36 \牧野(https://www.oschina.net/action/GoToLink?urlhttps%3A%2F%2Fme.csdn.net%2Fdcrmg) 阅读数:59593

KVM调整cpu和内存

一.修改kvm虚拟机的配置1、virsheditcentos7找到“memory”和“vcpu”标签,将<namecentos7</name<uuid2220a6d1a36a4fbb8523e078b3dfe795</uuid