Java并发编程的艺术(十)——线程池
线程池的作用
- 降低资源消耗。重复利用已有线程,减少线程的创建和销毁造成的消耗。
- 提高响应速度。当有任务需要处理的时候,就不用再花费重新创建线程的时间了。
- 提高线程的可管理性。不合理利用线程,会浪费资源,影响系统稳定,线程池可以对线程进行统一分配、调优和监控。
线程池的实现原理
线程池由两种觉得组成:多个工作线程和一个阻塞队列。
- 工作线程:运行在线程池中,当需要执行任务的时候,就会被用来执行任务。线程池中还会被划分出一个核心线程池。
- 阻塞队列:当核心线程池中的工作线程数量满了,新加入的任务就会进入阻塞队列。

执行流程:

线程池使用方法
创建线程池
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, timeUnit, runnableTaskQueue, handler);
corePoolsize:线程池的基本大小,通过设置这个值,将线程池的线程数量维持在某一大小。
maximumPoolSize:最大线程数量,这个线程池可以运行的线程最大数量。如果阻塞队列满了,而最大线程数量没有满,那么线程池就会创建新的线程来执行任务。如果最大线程数满了,那么就无法执行任务。
keepAliveTime:空闲线程的存活时间,如果运行的线程数量超过核心线程数量,同时空闲的线程存活时间大于这个值,那么超时的空闲线程就会被销毁。如果任务很多,每个任务的执行时间很短,就可以调大这个值,以提高线程的利用率。
timeUnit:keepAliveTime的单位。
runnableTaskQueue:任务队列, 用于保存等待执行任务的阻塞队列,可以有一下几个选择:
- ArrayBlockingQueue:基于数组结构的阻塞队列,FIFO;
- LinkedBlockingQueue:基于链表,吞吐量高于前者,FIFO;
- SynchronousQueue:没有空间的存储队列,每一个插入操作都必须等到其他线程的调用移除,不然插入操作本身处于阻塞。吞吐量高于前者;
- PriorityBlockingQueue:具有优先级的无限阻塞队列。
handler: 饱和策略,如果实际运行的线程数达到最大值,则执行饱和策略,JDK提供下面4中策略:
- AbortPolicy:直接抛出异常,默认策略。
- CallerRunsPolicy:只用调用者所在的线程执行任务。
- DiscardOldestPolicy:丢弃任务队列中最久的任务。
- DiscardPolicy:丢弃新来的任务。
任务提交
提交任务的两种方法:
excute() :不需要返回值的任务。可以提交Runnable接口的任务。执行过程中无法抛出异常。
submit():需要返回值的。可以提交Callable接口的任务。然会Future对象,通过get获取。
threadsPool.excute(new Runnable() {
@Override
public void run() {
dosometing();
}
}
Future<Object> future = executor.submit(harReturnValuetask);
try{
Object s = future.get();
} catch(InterruptedException e) {
dosomething();
} catch(ExcutionException e) {
dosomething();
} finally {
excutor.shutdown();
}
关闭线程池
两种方法关闭线程,都是通过遍历线程池中的工作线程,然后逐一调用interrupt方法来中断,无法响应中断的线程可能不能关闭。
- shutdown():将线程池的状态改为关闭,中断没有执行任务的线程和取消阻塞线程中的等待任务,然后等待其他线程执行任务完毕之后才关闭线程。
- shutdownNow():首先将线程池状态改为关闭,然后停止正在运行或没有运行的任务。
合理配置线程池
主要思想:根据任务的特性来配置线程池的参数。
任务的硬件资源需求
- CPU密集型任务
配置尽可能小的线程池,如CPU核心数+1的线程池。这样可以减小切换上下文带来的开销。 - IO密集型任务
配置尽可能大的线程池,如CPU核心数*2的线程池。这样当线程任务在等待IO的时候,CPU可以做别的事情了。 - 混合型任务
将任务再分为CPU密集和IO密集,然后再用不同的线程池运行,只要两个任务执行的时间相差不是太大,分解后执行的吞吐量将高于串行执行的吞吐量。如果差距很大,时间短的任务会等待时间长的,还要加上任务拆分与合并的开销,更浪费资源。
任务的优先级
利用PriorityBlockingQueue来处理。让优先级高的任务先执行。
任务的执行时间
不同执行时间的任务交给不同规模的线程池,或者交给优先级队列,让时间短的先执行。
任务的依赖性
例如依赖于数据库连接池的任务,需要等待数据库的返回才能继续执行,所以,开多一点的线程数,这样提高CPU的使用效率。
使用有界队列
能增加系统的稳定性和预警能力。要设置大一点,这样在例如SQL操作的时候,可以有足够的线程可以等待,从而也不浪费资源。
Java并发编程的艺术(十)——线程池的更多相关文章
- Java并发编程的艺术(十)——线程池(1)
线程池的作用 减少资源的开销 减少了每次创建线程.销毁线程的开销. 提高响应速度 每次请求到来时,由于线程的创建已经完成,故可以直接执行任务,因此提高了响应速度. 提高线程的可管理性 线程是一种稀缺资 ...
- Java并发编程的艺术(十一)——线程池(2)
Executor两级调度模型 在HotSpot虚拟机中,Java中的线程将会被一一映射为操作系统的线程. 在Java虚拟机层面,用户将多个任务提交给Executor框架,Executor负责分配线程执 ...
- Java 并发编程——Executor框架和线程池原理
Eexecutor作为灵活且强大的异步执行框架,其支持多种不同类型的任务执行策略,提供了一种标准的方法将任务的提交过程和执行过程解耦开发,基于生产者-消费者模式,其提交任务的线程相当于生产者,执行任务 ...
- [Java并发编程(二)] 线程池 FixedThreadPool、CachedThreadPool、ForkJoinPool?为后台任务选择合适的 Java executors
[Java并发编程(二)] 线程池 FixedThreadPool.CachedThreadPool.ForkJoinPool?为后台任务选择合适的 Java executors ... 摘要 Jav ...
- [Java并发编程(一)] 线程池 FixedThreadPool vs CachedThreadPool ...
[Java并发编程(一)] 线程池 FixedThreadPool vs CachedThreadPool ... 摘要 介绍 Java 并发包里的几个主要 ExecutorService . 正文 ...
- Java 并发编程——Executor框架和线程池原理
Java 并发编程系列文章 Java 并发基础——线程安全性 Java 并发编程——Callable+Future+FutureTask java 并发编程——Thread 源码重新学习 java并发 ...
- Java并发编程的艺术(六)——线程间的通信
多条线程之间有时需要数据交互,下面介绍五种线程间数据交互的方式,他们的使用场景各有不同. 1. volatile.synchronized关键字 PS:关于volatile的详细介绍请移步至:Java ...
- Java并发编程之深入理解线程池原理及实现
Java线程池在实际的应用开发中十分广泛.虽然Java1.5之后在JUC包中提供了内置线程池可以拿来就用,但是这之前仍有许多老的应用和系统是需要程序员自己开发的.因此,基于线程池的需求背景.技术要求了 ...
- Java并发编程、多线程、线程池…
<实战java高并发程序设计>源码整理https://github.com/petercao/concurrent-programming/blob/master/README.md Ja ...
随机推荐
- CentOS GRUB损坏修复方法
前言 博客很久没有更新了,一个原因就是原来存放部署博客的环境坏了,硬盘使用的是SSD,只要读取到某个文件,整个磁盘就直接识别不到了,还好博客环境之前有做备份,最近一直没有把部署环境做下恢复,今天抽空把 ...
- 修改centos6启动动画(plymouth方式)
centos6默认的启动动画是一个白蓝色的进度条,背景全黑色,现在需要对centos的启动动画进行定制 在查询了一些资料以后,发现有一个软件是可以对启动动画进行定制的,名字叫plymouth 这个在c ...
- python学习--sys.argv
sys.argv是获取命令行参数的: sys.argv[0]表示代码本身文件路径:从1开始获取参数. import sysprint (sys.argv[0])count = int(sys.argv ...
- 进程池与回调函数与正则表达式和re爬虫例子
# 使用进程池的进程爬取网页内容,使用回调函数处理数据,用到了正则表达式和re模块 import re from urllib.request import urlopen from multipro ...
- Frogs‘ Neighborhood(POJ 1659 C/C++)
poj 1659 Description 未名湖附近共有N个大小湖泊L1, L2, ..., Ln(其中包括未名湖),每个湖泊Li里住着一只青蛙Fi(1 ≤ i ≤ N).如果湖泊Li和Lj之间有水路 ...
- SSTI Flask
1.什么是SSTI?什么是Flask? SSTI称为服务端模板注入,主要为Python.Java.PHP的框架在使用渲染函数时,由于代码不规范或者对于用户输入过于信任而导致产生了SSTI.类似于S ...
- 画echart图时,安卓手机正常,苹果手机上tooltip 在坐标轴下一层
问题: 解决: 在tooltip 中添加如下代码: tooltip:{ position:function(point, params , dom, rect, size){ dom.style.tr ...
- php7的Opcache getshell
OPcache基础 OPcache是一种通过解析的PHP脚本预编译的字节码存放在共享内存中来避免每次加载和解析PHP脚本的开销,解析器可以直接从共享内存读取已经缓存的字节码,从而大大提高了PHP的执行 ...
- 全网最全!这份深入讲解jdk和jvm原理的笔记,刷新了我对JVM的认知
前言 前两天和朋友探讨技术的时候有聊到JVM和JDK这一块,聊到这里两个人就像高山流水遇知音那是根本停不下来,事后我想着趁现在印象还比较深刻就把这些东西整理起来分享给大家来帮助更多的人吧.话不多说,满 ...
- synchronized关键字的内存语义
以下内容摘自:Java并发编程之美 加锁和释放锁的语义:当获取锁以后会清空锁块内本地内存中将会被用到的共享变量,在使用这些共享变量的时从主内存进行加载,在释放锁时将本地内存中修改的 共享变量刷新到主内 ...