线程是珍贵的资源,每创建一个线程jvm都要给予分配栈空间和堆空间,创建的过程是一个比较消耗性能的过程。而且过多的线程可能会引起栈溢出或内存溢出等异常,甚至是文件句柄不够(linux默认非root用户文件句柄为1024)。
基于以上考虑,对线程进行有效的管控是一个提高性能必要的选择,特别是高并发环境下。jdk为我们提供了线程池来管理线程。
1. 线程池原理
2. 饱和策略
- AbortPolicy:默认的策略,直接抛出RejectedExecutionException异常
- CallerRunsPolicy:这个策略比较有意思,看源码可以直接内部直接调用了run方法,这相当于直接让调用execute方法的线程直接执行这个方法了。
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
- DiscardOldestPolicy:将等待队列中的第一个任务抛弃,并重试execute方法,这个方式在高并发环境,并且大部分任务的执行时间都比较长的情况下,可能会造成现场饥饿死锁
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
- DiscardPolicy:就是什么都不干,抛弃当前任务
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
3.jdk内置的几种线程池
- CachedThreadPool:
没有线程上线的线程池,线程空闲60s就回收,使用该线程要比较小心,假设创建的线程过多会使系统崩溃,不建议在高并发情况下使用。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
- FixedThreadPool:
固定大小的线程池,使用该线程池的时候,一定要注意分配的线程数数量,避免资源的浪费或者资源的不足情况。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- SingleThreadExecutor:
只有单个线程,该线程池能保证任务按FIFO的顺序执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
- newScheduledThreadPool:
创建一个定时任务线程池
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
ps:在jdk1.8下的源码