-
一 使用线程池的好处
池化技术应用:线程池、数据库连接池、http连接池等等。
池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。
线程池提供了一种限制、管理资源的策略。 每个线程池还维护一些基本统计信息,例如已完成任务的数量。
使用线程池的好处:
二 Executor框架
Executor框架不仅包括了线程池的管理,还提供了线程工厂、队列以及拒绝策略等,让并发编程变得更加简单。
Executor框架的使用示意图
import java.util.concurrent.ArrayBlockingQueue;import java.util.concurrent.ThreadPoolExecutor;import java.util.concurrent.TimeUnit;public class ThreadPoolExecutorDemo {private static final int CORE_POOL_SIZE = 5;private static final int MAX_POOL_SIZE = 10;private static final int QUEUE_CAPACITY = 100;private static final Long KEEP_ALIVE_TIME = 1L;public static void main(String[] args) { ThreadPoolExecutor executor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.SECONDS,new ArrayBlockingQueue<>(QUEUE_CAPACITY),new ThreadPoolExecutor.CallerRunsPolicy());//执行线程代码executor.shutdown(); } }
CORE_POOL_SIZE:核心线程数定义了最小可以同时运行的线程数量。
MAX_POOL_SIZE:当队列中存放的任务到达队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
QUEUE_CAPACITY:当新任务加入是会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,任务就会被存放到队列中。
KEEP_ALIVE_TIME:当线程池中的线程数量大于核心线程数时,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过KEEP_ALIVE_TIME才会被回收销毁。
ThreadPoolExecutor.CallerRunsPolicy():调用执行自己的线程运行任务,也就是直接在调用execute方法的线程运行(run)被拒绝的任务,如果执行程序已关闭,则会丢弃任务。因此这种策略会降低新任务的提交速度,影响程序的整体性能。另外,这个策略喜欢增加队列容量。如果应用程序可以承受此延迟并且不能任务丢弃一个任务请求的话,可以选择这个策略。
线程池分析原理
三 线程池大小确定
有一个简单且使用面比较广的公式:
CPU密集型任务(N+1):这种任务消耗的主要是CPU资源,可以将线程数设置为N(CPU核心数)+1,比CPU核心数多出来一个线程是为了防止线程偶发的缺页中断,或者其他原因导致的任务暂停而带来的影响。一旦任务停止,CPU就会出于空闲状态,而这种情况下多出来一个线程就可以充分利用CPU的空闲时间。
I/O密集型(2N):这种任务应用起来,系统大部分时间用来处理I/O交互,而线程在处理I/O的是时间段内不会占用CPU来处理,这时就可以将CPU交出给其他线程使用。因此在I/O密集型任务的应用中,可以配置多一些线程,具体计算方是2N。
降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
提高响应速度:当任务到达时,可以不需要等待线程创建就能立即执行。
提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,监控和调优。
主线程首先要创建实现Runnable或Callable接口的任务对象。
-
把创建完成的实现Runnable/Callable接口的对象直接交给ExecutorService执行:
ExecutorService.execute(Runnable command)或者ExecutorService.sumbit(Runnable command)或ExecutorService.sumbit(Callable <T> task).
如果执行ExecutorService.submit(...),ExecutorService将返回一个实现Future接口的对象。最后,主线程可以执行FutureTask.get()方法来等待任务执行完成。主线程也可以执行FutureTask.cancel()来取消次任务的执行。