多线程与并发 | 线程池
1. 线程池的优势
- 降低系统
资源消耗
,通过重用已存在的线程,降低线程创建和销毁造成的消耗 - 提高系统
响应速度
,当有任务到达时,通过复用已存在线程,无需等待新线程的创建便能立即执行 - 方便线程
并发数的管控
,因为线程若是无限制的创建,可能会导致内存占用过多而产生OOM,并且会造成cpu过度切换 - 提供更强大的功能,比如延时
定时线程池
2. 线程池的主要参数
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
2-1. corePoolSize
当向线程池提交一个任务时,若线程池已创建的线程数小于corePoolSize,即便此时存在空闲线程,也会通过创建一个新线程来执行该任务.可以通过prestartCoreThread()
方法来提前启动线程池中的核心线程
2-2. maximumPoolSize
线程池所允许的最大线程个数.当队列满了
,且已创建的线程数小于maximumPoolSize,则线程池会创建新的线程来执行任务
2-3. keepAliveTime
当线程池中线程数大于核心线程数
时,线程的空闲时间如果超过线程存活时间,则这个线程就会被销毁,知道线程池中的线程数小于等于核心线程数
2-4. workQueue
等待队列
用于传输和保存等待执行任务的阻塞队列
ArrayBlockingQueue
LinkedBlockingQueue
PriorityBlockingQueue
SynchronizedQueue
2-5. threadFactory
用于创建新线程
2-6. handler
拒绝策略
当线程池和队列都满了,再加入线程会执行此策略
-
AbortPolicy
直接抛出异常
,提示线程已满(默认
拒绝策略) -
DisCardPolicy
不执行新任务,也不抛出异常 -
DisCardOldSetPolicy
将消息队列中的第一个任务替换为当前新任务 -
CallerRunsPolicy
直接调用execute来执行当前任务
3. execute和submit
-
public void execute(Runnable command)
execute没有返回值 -
submit()
更通用一些,可以传入Runnable和Callable
submit(Runnable task,T result)
submit(Runnable task) 没有返回值,相当于execute(task)
submit(Callable task)返回future,通过future.get(),阻塞直到获取返回值
4. 如何配置线程池
-
CPU密集型任务
一般线程数为CPU核心数 + 1
. 因为CPU密集型任务使得CPU使用率很高,若开过多的线程数,会造成CPU过度切换 -
IO密集型任务
一般为2 * CPU核心数
. IO密集型任务CPU使用率不高,因此可以让CPU在等待IO的时候有其他线程去处理别的任务,充分利用CPU时间 -
混合型任务
将任务分成IO密集型和CPU密集型任务,然后分别用不同的线程池
去处理
5. java中提供的线程池
newCachedThreadPool,newFixedThreadPool,newSchduledThreadPool,newSingleThreadExecutor
方法 | core | max | keepAliveTime | workQueue |
---|---|---|---|---|
newCachedThreadPool |
0 | Integer.MAX_VALUE |
60s | SynchronousQueue |
newFixedThreadPool |
nThreads | nThreads | 0 | LinkedBlockingQueue(Integer.MAX_VALUE ) |
newSingleThreadExecutor |
1 | 1 | 0 | LinkedBlockingQueue(Integer.MAX_VALUE ) |
newScheduledThreadPool |
corePoolSize | Integer.MAX_VALUE | 0 | DelayedWorkQueue |