线程池
并行与并发
- 并发:CPU不断切换,给人同时执行多个任务的感觉。
- 并行:CPU多个实例,真正同时执行多个任务的状态。
进程与线程
任何事物都需要不同粒度的划分。
豆腐要一块一块卖。
程序要一个一个跑。
这里的一个一个跑,不是指串行,而是指资源的划分,执行程序的界定。
什么是进程?
进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。
在早期面向进程设计的计算机结构中,进程是程序的基本执行实体。
在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。
动态性:进程的实质是程序在多道程序系统中的一次执行过程,进程是动态产生,动态消亡的。
并发性:任何进程都可以同其他进程一起并发执行。
独立性:进程是一个能独立运行的基本单位,同时也是系统分配资源和调度的独立单位。
异步性:由于进程间的相互制约,使进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进。
结构特征:进程由程序、数据和进程控制块三部分组成。
什么是线程?
线程(英语:thread)是操作系统能够进行运算调度的最小单位。
它被包含在进程之中,是进程中的实际运作单位。
一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程并行执行不同的任务。
在Unix System V及SunOS中也被称为轻量进程(lightweight processes),但轻量进程更多指内核线程(kernel thread),而把用户线程(user thread)称为线程。
Executor
- 线程池就是线程的集合,线程池集中管理线程,以实现线程的重用,降低资源消耗,提高响应速度等。
- JDK1.5之前,线程用于执行异步任务,单个的线程既是工作单元也是执行机制。
- 从JDK1.5开始,把工作单元与执行机制分离开,Executor框架诞生。它是一个用于统一创建与运行的接口。Executor框架实现的就是线程池的功能。
- Executor框架包括3个部分:
- 任务:即工作单元,包括被执行任务需要的接口,Runnable接口或者Callable接口。
- 任务的执行:把任务分派给多个线程的执行机制,
- 任务异步计算的结果:包括Future接口及实现了Future接口的FutureTask类。
Future提供了三种功能:
1. 判断任务是否完成。
2. 能够中断任务。
3. 能够获取任务执行结果。
CompletionService
- ExecutorService 启动多个Callable时,每个Callable返回一个Future,而当我们执行Future的get方法获取结果时,可能拿到的Future并不是第一个执行完成的Callable的Future,就会进行阻塞,从而不能获取到第一个完成的Callable结果,那么这样就造成了很严重的性能损耗问题。
- CompletionService会根据线程池中Task的执行结果按执行完成的先后顺序排序,任务先完成的可优先获取到。
线程池
-
线程思想:并发执行,分而治之(解决效率问题)。
-
线程池思想:每个请求一个线程(thread-per-request),有线程的创建与销毁所耗费资源和时间 远大于 实际作业占用时间和资源,造成资源浪费,所以要减少线程创建和销毁的频次。
-
线程池衍生问题:切换过度、创建无度,导致资源过载。解法:估算、预分配。
-
设定线程个数、资源使用情况,避免线程频繁创建与删除。一般的线程池主要分为以下 4 个组成部分:
- 线程池管理器:用于创建并管理线程池。
- 工作线程:线程池中的线程。
- 任务接口:每个任务必须实现的接口,用于工作线程调度其运行。
- 任务队列:用于存放待处理的任务,提供一种缓冲机制。
- Java 中的线程池是通过 Executor 框架实现的,该框架中用到了 Executor,Executors,ExecutorService,ThreadPoolExecutor ,Callable 和 Future、FutureTask 这几个类。
- ThreadPoolExecutor 的构造方法如下:
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,Executors.defaultThreadFactory(),defaultHandler); }
- corePoolSize:指定了线程池中的线程数量。
- maximumPoolSize:指定了线程池中的最大线程数量。
- keepAliveTime:当前线程池数量超过 corePoolSize 时,多余的空闲线程的存活时间,即多次时间内会被销毁。
- unit:keepAliveTime 的单位。
- workQueue:任务队列,被提交但尚未被执行的任务。
- threadFactory:线程工厂,用于创建线程,一般用默认的即可。
- handler:拒绝策略,当任务太多来不及处理,如何拒绝任务。
-
线程池中的线程已经用完了,无法继续为新任务服务,同时,等待队列也已经排满了,再也塞不下新任务了。这时候我们就需要拒绝策略机制合理的处理这个问题。
-
JDK 内置的拒绝策略如下:
- AbortPolicy : 直接抛出异常,阻止系统正常运行。
- CallerRunsPolicy : 只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。显然这样做不会真的丢弃任务,但是,任务提交线程的性能极有可能会急剧下降。
- DiscardOldestPolicy : 丢弃最老的一个请求,也就是即将被执行的一个任务,并尝试再次提交当前任务。
- DiscardPolicy : 该策略默默地丢弃无法处理的任务,不予任何处理。如果允许任务丢失,这是最好的一种方案。以上内置拒绝策略均实现了 RejectedExecutionHandler 接口,若以上策略仍无法满足实际需要,完全可以自己扩展 RejectedExecutionHandler 接口。
常用线程池?各自特点
- newCachedThreadPool(数目上限为 Interger.MAX_VALUE,线程默认一分钟空闲则销毁)newFixedThreadPool(maxnum,队列,空闲也不会释放)newSingleThreadExecutor(始终且仅有一个工作线程,保证任务有序(FIFO, LIFO, 优先级)执行)newScheduleThreadPool(创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。)
- 线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。
- 当调用 execute() 方法添加一个任务时,线程池会做如下判断:
a) 如果正在运行的线程数量小于 corePoolSize,那么马上创建线程运行这个任务;
b) 如果正在运行的线程数量大于或等于 corePoolSize,那么将这个任务放入队列;
c) 如果这时候队列满了,而且正在运行的线程数量小于 maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;
d) 如果队列满了,而且正在运行的线程数量等于 maximumPoolSize,那么线程池会抛出异常 RejectExecutionException。 - 当一个线程完成任务时,它会从队列中取下一个任务来执行。
- 当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于 corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到 corePoolSize 的大小。
线程池具体应用?
- 风险:死锁、资源过载、并发错误、线程泄漏(线程没有返回池子)、请求过载(队列很长)。
- 参数设定线程池个数:考虑CPU处理器个数 和 任务性质。一个计算性质任务队列:线程池N或N+1个线程一般会获得最大CPU利用率。(N个处理器)。涉及I/O慢操作任务:等待时间(WT)与服务时间(ST)。大约 N*(1+WT/ST) 个线程。
线程特性?
- 原子性:一个操作不可中断,要么全做,要么不做。
- 可见性:当一个线程修改了共享变量后,其他线程能够立即得知这个修改。
- 有序性:锁在同一时刻只能由一个线程进行获取,当锁被占用后,其他线程只能等待。因此,synchronized语义就要求线程在访问读写共享变量时只能“串行”执行。如果在本线程内观察,所有的操作都是有序的;如果在一个线程观察另一个线程,所有的操作都是无序的。
Reference
- https://www.jianshu.com/p/cf57726e77f2(三大性质总结:原子性,有序性,可见性)
- https://blog.csdn.net/GZHarryAnonymous/article/details/100542611?spm=1001.2014.3001.5502(AQS理解)
- https://blog.csdn.net/GZHarryAnonymous/article/details/100164649?spm=1001.2014.3001.5502(计算机基础知识复习)
- https://docs.oracle.com/javase/8/docs/api/(Java™ Platform, Standard Edition 8 API Specification)
- https://blog.csdn.net/c10WTiybQ1Ye3/article/details/78098544(线程池,这一篇或许就够了)
- https://www.cnblogs.com/wxd0108/p/5479442.html(Java中的多线程你只要看这一篇就够了)