李林超博客
首页
归档
留言
友链
动态
关于
归档
留言
友链
动态
关于
首页
Java
正文
线程池简单介绍
Leefs
2020-03-02 PM
2283℃
0条
# 线程池简单介绍 ### 一、线程池的优势 线程池做的工作主要是控制运行的线程的数量,**处理过程中将任务放入队列**,然后在线程创建后启动这些任务,**如果线程数量超过了最大数量超出数量的线程排队等候**,等其它线程执行完毕,再从队列中取出任务来执行。 他的主要特点为:线程复用;控制最大并发数;管理线程。 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。 第三:提高线程的可管理性。线程是稀缺资源,如无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。 ### 二、线程池架构实现 Java中的线程池是通过executor框架实现的,该框架中用到了Executor接口、Executors类、ThreadPoolExecutor类和ScheduledThreadPoolExecutor类。Executors和Executor的关系等同于从Collections与Collection和Arrays与Array,前者提供了许多辅助工具类可以很方便的使用。 ![线程池简单介绍01.png][1] ### 三、常用方法 线程池的有5种,其中最常用的有以下三种 + 1.Executors.newFixedThreadPool(int) **固定线程个数:执行长期任务,性能好很多** 具体实现: ```java public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue
()); } ``` 主要特点: 1. 1.创建一个`定长线程池`,可控制线程最大并发数,超出的线程会在队列中等待 2. 2.newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值相等,`它使用的LinkedBolickingQueue` + 2.Executors.newSingleThreadExecutor() **只有一个线程 适用于单个任务现行执行的场景** 具体实现: ```java public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue
())); } ``` **主要特点:** 1. 1.创建**一个单线程化**的线程池,他只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行 2. 2.newSingleThreadExector将corePoolSize和maximumPoolSize都设置为1,`它使用的LinkedBlockingQueue` + 3.Executors.newCachedThreadPool()动态扩大线程数,适用于执行很多短期异步的小程序或负载比较轻的服务 ```java public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue
()); } ``` 主要特点: 1. 1.创建一个**可缓存线程池**,如果线程池长度超过处理需要,可灵活回收空线程,若无可回收,则新建线程。 2. 2.newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用的SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲60秒,就销毁线程。 以上三段代码可知,线程池的构造都是从一个方法而来: `ThreadPoolExecutor` **代码测试** ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; //使用Java多线程的方式 线程池 public class MyThreadPoolDemo { public static void main(String[] args) { //ExecutorService threadPool = Executors.newFixedThreadPool(5); ExecutorService threadPool = Executors.newSingleThreadExecutor();//一池1个处理线程。 //ExecutorService threadPool = Executors.newCachedThreadPool();//一池N个处理线程 try { //模拟10个用户来办理业务,每个用户就是一个来自外部的请求线程 for(int i=1;i<=10;i++){ threadPool.execute(()->{ System.out.println(Thread.currentThread().getName()+"\t 办理业务"); }); } }catch (Exception e){ e.printStackTrace(); }finally { threadPool.shutdown(); } } } ``` **运行结果** ```java 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 办理业务 ``` #### ThreadPoolExector 由以上三段代码可知,在Exectors内部创建线程池时候,实际创建的都是一个ThreadPoolExecutor对象,只是对ThreadPoolExecutor构造方法,进行了默认值的设定。其构造方法如下: ```java public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue
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.acc = System.getSecurityManager() == null ? null : AccessController.getContext(); this.corePoolSize = corePoolSize; this.maximumPoolSize = maximumPoolSize; this.workQueue = workQueue; this.keepAliveTime = unit.toNanos(keepAliveTime); this.threadFactory = threadFactory; this.handler = handler; } ``` **参数含义如下:** > + 1.corePoolSize:常驻核心线程数 > + 2.maximumPoolSize:线程池中能够容纳同时执行的最大线程数,必须大于等于1 > + 3.workQueue:任务队列,被提交到那时尚未被执行的任务 > + 4.keepAliveTime:线程空闲时间长度,即非核心线程,当队列中排队的任务过多,会创建出来小于等于最大线程数的线程作为临时线程来执行队列中的任务,如果这类临时线程空闲时间超过keepAliveTime,则会被销毁,只剩下核心线程数 > + 5.unit:空闲时间的单位 > + 6.threadFactory:线程工厂,用于创建线程一般用默认的即可 > + 7.RejectedExecutionHandler:拒绝策略,表示当队列满了并且工作线程大于等于线程池的最大线程数时,应对再接收到的线程的策略 ### 四、参数使用场景: ![线程池简单介绍02.png][2] > 1. 1.创建线程池后,等待提交过来的任务请求 > > 2. 2.当调用execute()方法添加一个请求任务时,线程池会做如下判断: > > 2.1 如果正在运行的线程数小于corePoolSize时,创建新的线程运行这个任务 > > 2.2 如果正在运行的线程数大于等于corePoolSize,那么把任务放入**队列**中 > > 2.3 如果这时队列满了,且正在运行的线程数小于maximumPool,那么还要创建**非核心线程**运行这个任务 > > 2.4 如果线程数已经达到maximumPool,那么线程池会启动`饱和拒绝策略`来执行。 > > 3. 3.当一个线程执行任务完成,他会从队列中取出下一个任务来执行 > 4. 4.当一个线程空闲超过`keepAliveTime`,线程池会判断 如果当前运行线程数大于corePoolSize,则线程被销毁 > 5. 5.线程池所有任务完成后,最终会收缩到corePoolSize() ### 五、线程拒绝策略 当队列满了而且线程数已经达到maximumPoolSize,接下来的线程会受到拒绝策略的管控拒绝策略有四种: + 1.AbortPolicy(默认):直接抛出RejectedExecutionException一场阻止系统正常运行 + 2.CallerRunsPolicy:“调用者运行”一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者那里,从而降低新任务的流量 + 3.DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务 + 4.DiscardPolicy:直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种方案 ### 线程池配置合理线程数 **CPU密集型** 该任务需要大量的运算,并且没有阻塞,CPU一直全速运行,CPU密集任务只有在真正的多核CPU上才可能通过多线程加速 CPU密集型任务配置尽可能少的线程数量:CPU核数+1个线程的线程池 **IO密集型** IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如CPU核数*2 + 某大厂设置策略:IO密集型时,大部分线程都阻塞,故需要多配置线程数: 公式:CPU核数/1-阻塞系数 阻塞系数:0.8-0.9 比如8核CPU: 8/1-0.9 = 80 个线程数 [1]: https://lilinchao.com/usr/uploads/2020/03/1615208499.png [2]: https://lilinchao.com/usr/uploads/2020/03/3974445323.png
标签:
并发编程
,
线程池
非特殊说明,本博所有文章均为博主原创。
如若转载,请注明出处:
https://lilinchao.com/archives/669.html
上一篇
【转载】ReentrantLock和synchronized的比较
下一篇
post请求方式分析
评论已关闭
栏目分类
随笔
2
Java
326
大数据
229
工具
31
其它
25
GO
47
NLP
4
标签云
MyBatisX
字符串
队列
散列
前端
栈
Nacos
Hadoop
人工智能
Netty
MyBatis-Plus
GET和POST
Linux
Hive
Spring
HDFS
并发线程
Golang基础
设计模式
Jenkins
Spark SQL
查找
Quartz
Kibana
Spark
微服务
Java阻塞队列
Zookeeper
ajax
Stream流
友情链接
申请
范明明
庄严博客
Mx
陶小桃Blog
虫洞
评论已关闭