为什么要用线程池?
线程是不是越多越好?
线程在java中是一个对象,更是操作系统的资源,线程创建、销毁需要时间。如果创建时间+小会时间>执行任务时间就很不合算。
java对象占用堆内存,操作系统线程占用系统内存,根据jvm规范,一个线程默认最大栈大小1M,这个栈空间是需要从系统内存中分配的。线程过多,会消耗很多的内存。
操作系统需要频繁切换线程上下文(每个线都想被运行),影响性能。
线程池的推出,就是为了方便边的控制线程数量。
线程池
线程池基本概念
线程池包括以下四个基本组成部分:
线程池管理器:用于创建并管理线程池,包括创建线程池,销毁线程池,添加新任务;
工作线程:线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
任务接口:每个任务必须实现的借口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
任务队列:用于存放没有处理的任务。提供一种缓冲机制。
线程池接口定义和实现类
可以认为ScheduledThreadPoolExector是最丰富的实现类。
ExecutorService
ScheduledExecutorService
线程池工具类
在使用过程中,可以自己实例化线程池,也可以用Executors创建线程池的工厂累,常用方法如下:
newFixedThreadPool(int nThreads)
创建一个固定大小、任务队列容量误解的线程池。核心线程数=最大线程数。
newCachedThreadPool()
创建的是一个大小无界的缓冲线程池。它的任务队列是一个同步队列。任务加入到池中,如果池中有空闲线程,则用空闲线程执行,如无则创建新线程执行。池中的线程空闲时间超过60秒,将被销毁释放。线程数随任务的多少变化。适用于执行耗时较小的异步任务。池的核心线程数=0,最大线程=Integer.MAX_VALUE
newSingleThreadExecutor()
只有一个线程来执行无界任务队列的单一线程池。该线程池确保任务加入的顺序一个一个一次执行。当唯一的线程因任务异常中止时,将创建一个新的线程来继续执行后续的任务。与newFixedThreadPool(1)的区别在于,单一线程池的池大小在newSingleThreadExecutor方法中硬编码,不能再改变的。
newScheduledThreadPool(int corePoolSize)
能定时执行任务的线程池。该池的核心线程数由参数指定,最大线程数=Integer.MAX_VALUE
任务线程池执行过程
如何确认合适的线程数量?
如果是CPU密集型应用,则线程池大小设置为N+1 (N为CPU总核数)
如果是IO密集型应用,则线程池大小设置为2N+1 (N为CPU总核数)
线程等待时间(IO)所占比例越高,需要越多线程。
线程CPU时间所占比例越高,需要越少线程。
一个系统最快的部分是CPU,所以决定一个系统吞吐量上限的是CPU。增强CPU处理能力,可以提高系统吞吐量上限。但根据短板效应,真实的系统吞吐量并不能单纯根据CPU来计算。那要提高系统吞吐量,就需要从“系统短板”(比如网络延迟、IO)着手:
尽量提高短板操作的并行化比率,比如多线程下载技术;
增强短板能力,比如用NIO替代IO;
线程池的使用分析
领取专属 10元无门槛券
私享最新 技术干货