EP40-ThreadPool
什么时候用线程池
线程池是为了在有很多线程的时候方便地管理线程。
手工定义的线程不能重复利用,每次使用都需要重新申请资源、启动线程,而线程创建和启动需要时间。
线程池通过缓存池预先创建一部分线程,需要的时候直接从里面取;用完之后线程池又把它回收再利用。
所以,应用场景是对性能要求较高、线程请求比较多的情况。
有三个好处;
- 通过reuse降低创建/销毁线程造成的资源消耗
- 线程提前创建好了,任务达到时,响应速度快。
- 通过统一分配和控制,优化稳定性、资源消耗。
线程池类型
创建线程池可以直接用
new ThreadPoolExecutor(corePoolSize, maximumPoolSize,
keepAliveTime, milliseconds,runnableTaskQueue, threadFactory,handler);
来构造,参数分别是:
-
int corePoolSize
:pool中keep的线程数量,包括idle的 -
int maximumPoolSize
:允许的最多线程数量 -
long milliseconds
当线程数量超过了corePoolSize,超出部分的idle线程允许的最大停留时间 -
BlockingQueue<Runnable> workQueue
等待执行的Task队列
也可以用Executors
构造一种具体的线程池:
/**
* JDStock的RunningContext里的全局线程池
*/
private static ThreadPoolExecutor sThreadPool = (ThreadPoolExecutor) Executors
.newCachedThreadPool();
Executors.java类里的静态方法,可以创建很多不同类型的线程池。介绍三种常用的线程池。
-
SingleThreadExecutor
只包含一个线程的线程池。适用于每次只使用一个线程的情况。任务会依次执行。
创建方法:Executors.newSingleThreadExecutor()
。 -
CachedThreadPool
这个线程池,只要有创建线程的需要,就创建新的线程。会重用之前构造的可用的线程,如果有需要,就用ThreadFactory
创建新的线程。如果线程数目过多,不适合用这个线程池,因为会占用很多资源。适合线程数量不是特别多的情况。
创建方法:Executors.newCachedThreadExecutor()
。 -
FixedThreadPool
重用(reuse)固定数量的线程。在任何时间点都只能有指定数量的线程处理任务。如果有其他任务提交了,所有的线程又都已经是active的了,这些任务(Task)会在队列里等着,直到有一个线程可用了。这个线程池会一直存在,直到执行了ExecutorService.shutdown
。
给线程池提交任务的方法
-
threadsPool.execute(new Runnable(){})
没有返回值。 -
ExecutorService
的submit
方法。会返回future
,用future.get()获取返回值,future.get()
会阻塞到任务完成,get(long timeout, TimeUnit unit)
会阻塞指定时间然后然会,无论任务有没有执行完。
线程池工作流程
ThreadPoolExecutor.java里的execute方法:
执行的三个step:
-
如果比
corePoolSize
个线程数少的线程正在运行,尝试用给的command创建一个新线程作为它的第一个task。否则进入2。 -
判断工作队列是否已满, 如果一个task可以成功加入队列,没满就把task加入这个队列。否则进入3。
-
不能入队,就判断整个线程池是否已满(
MaximumPoolSize
)。满了,就尝试创建一个新的线程。创建不了就拒绝task。
//ThreadPoolExecutor.java里的execute方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
Reference:
[1]http://ifeve.com/java-threadpool/