多线程(四):线程池

线程池的好处

1.降低资源的消耗

2.提高响应的速度

3.方便管理

4.线程复用、可以控制最大并发数、管理线程

new Thread()的弊端

1.每次new Thread()新建对象性能差

2.线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或OOM

3.缺乏更多功能,如定时执行、定期执行、线程中断

相比new Thread,Java提供的四种线程池的好处在于:

1.重用存在的线程,减少对象创建、消亡的开销,性能差

2.可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞

3.提供定时执行,定期执行、单线程、并发数控制等功能

Java通过Executors提供四种线程池

newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程

线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程

newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行

newSingleThreadExecutor

创建一个单线程的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO、LIFO、优先级)执行

七大参数

核心线程数(常驻)

最大线程数(不到万不得已不开放)

多余空闲线程存活时间

时间单位

线程工厂(用于创建线程)

阻塞队列(任务队列)

1.最大线程,扩容发生在阻塞队列队满之后,“加班”的线程

2.新创建的线程执行新提交的任务

拒绝策略

多余的无法加入阻塞队列的线程启动,实现RejectedExcutionHandler接口

AbortingPolicy:默认,直接抛出异常阻止系统正常运行

CallerRunsPolicy:将任务回退到调用者,“调用者运行”机制

DiscardOldestPolicy:抛弃队列中等待最久的任务,将新任务加到队列中

DiscardPolicy:直接丢弃任务,不予任何处理

配置线程池

1.CPU密集型任务配置尽可能少的线程数量:cpu核数*2

2.大部分线程都阻塞,故需要多配置线程数:cpu核数/(1-阻塞系数),阻塞系数在0.8-0.9之间


实际开发不建议使用Executor去创建,而是通过ThreadPoolExecutor方式,因为

1.FixedThreadPool和SingleThreadPool:允许请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM

2.CachedThreadPool和ScheduledThreadPool:允许创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM


新的任务提交到线程池,线程池的处理方式

第一步 :线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则执行第二步。

第二步 :线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里进行等待。如果工作队列满了,则执行第三步。

第三步 :线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务。

即任务处理优先级:核心线程池的线程 > 工作队列 > 线程池的线程 > 饱和策略


线程池工作队列的类型

1、ArrayBlockingQueue

是一个基于数组结构的有界阻塞队列,此队列按 FIFO(先进先出)原则对元素进行排序。

2、LinkedBlockingQueue

一个基于链表结构的阻塞队列,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列

3、SynchronousQueue

一个不存储元素的阻塞队列。每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQueue,静态工厂方法Executors.newCachedThreadPool使用了这个队列。

4、PriorityBlockingQueue

一个具有优先级的无限阻塞队列

你可能感兴趣的:(多线程(四):线程池)