线程池浅析
线程池顾名思义就是放线程的池子 Thread Pool。
那么为什么要有线程池呢?有些时候系统需要处理非常多的执行时间很短的请求,如果每一个请求都开启一个新的线程,则系统创建销毁线程的开销就太大了,甚至在创建和销毁线程的时间比任务的执行时间还长。
使用new Thread() 会有如下缺点:
1.创建和销毁线程耗费大量系统资源;
2.每次new Thread() 缺乏线程管理,被称为野线程,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪。
3.不利于扩展,如定期执行,定时执行,线程中断。
线程池主要解决了两个问题:
1.通过重用线程,达到减少每个线程创建和销毁的性能开销。
2.对线程进行一些维护,比如定时开始,周期执行,并发控制等。
3.可控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争。
Executor:JDK1.5引入,内部使用线程池机制,在java.util.cocurrent包下,Executor框架包括:线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable,ThreadPoolExecutor等。
Executors:工厂类 创建线程池
1.newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }
创建一个固定数目的线程池,内部维持一个无界的队列,当所有的线程都在处于活动状态时提交了其他任务,则新任务将在队列中等待直到线程可用。
2.newCachedThreadPool
public static ExecutorService newCachedThreadPool() { return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue<Runnable>()); }
创建一个根据需要创建新线程的线程池,但在可用时将重新使用以前构造的线程。
该线程池适用于执行许多短暂异步任务的程序,调用execute会重用以前构造的线程,如果没有可用的线程,则会创建新的线程,并将该线程添加到该池中,未使用60秒的线程将被终止并从缓存中删除。因此,长时间保持闲置的池将不会消耗任何资源。
3.newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
创建一个使用无界队列运行单个工作线程的执行程序,等效于newFixedThreadPool(1)
4.newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) { return new ScheduledThreadPoolExecutor(corePoolSize); }
创建一个线程池,可以调度命令在给定的延迟之后运行,或定期执行。
scheduleAtFixedRate(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
首次执行在 initialDelay 之后,第二次 执行时间为 initialDelay+delay*1 ….
scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit);
第一次的执行时间为 当前时间+ initialDelay,第二次为第一次执行结束时间+delay
ExecutorService 的submit() 和 execute()区别:
1.入参不同 submit可以接收Runnable或者Callable ,execute方法只能接收Runnable
2.返回值 submit 有返回值 而 execute没有返回值
3.submit方便处理Exception Future.get抛出的异常
Runnable和Callable的区别
Callable接口中的call方法是有返回值的,泛型可任意制定,但是Runnable接口中run方法是没有返回值的。
ThreadPoolExecutor:JDK提供的Executors工具类中创建的线程无法满足使用场景时,则需要手动配置和调优,则用到该池。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }
参数详解:
corePoolSize:核心线程数
maximumPoolSize:最大线程数
keepAliveTime:最大空闲时间
BlockingQueue:使用的任务队列
ArrayBlockingQueue:底层是数组的定长任务队列,必须制定长度,可设置是 否公平锁。默认先进先出
LinkedBlockingQueue:底层为链表的可定长任务队列,不指定长度则为无界, 可设置公平锁,默认为先进先出
SynchronousQueue:同步的没有缓冲的任务队列,只有当线程将队列中的take走之后,才能新加进来任务
defaultHandler:该参数代表的是拒绝策略,默认为AbortPolicy
可以通过设置改变
/**
* ThreadPoolExecutor.AbortPolicy() 抛出java.util.concurrent.RejectedExecutionException异常
* ThreadPoolExecutor.CallerRunsPolicy() 使用主线程跑当前任务
* ThreadPoolExecutor.DiscardOldestPolicy() 抛弃旧的任务
* ThreadPoolExecutor.DiscardPolicy() 抛弃当前的任务
*/