java中的线程池
Posted FreeFly辉
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中的线程池相关的知识,希望对你有一定的参考价值。
为什么要引入线程池
1、系统执行多任务时,会为每个任务创建对应的线程,当任务执行结束之后会销毁对应的线程,在这种情况下对象被频繁的创建和销毁。 2、当对线程象被频繁时会占用大量的系统资源,在并发的过程中会造成资源竞争出现问题。大量的创建线程还会造成混乱,没有一个统一的管理机制,容易造成应用卡顿。 3、大量线程对象被频繁销毁,将会频繁出发GC机制,从而降低性能。引入线程池的好处:
1、重用线程池中的线程,避免因频繁创建和销毁线程造成的性能消耗。 2、更加有效的控制线程的最大并发数,防止线程过多抢占资源造成的系统阻塞。 3、对线程进行有效的管理。阿里巴巴android开发手册,里面有
【强制】新建线程时,必须通过线程池提供(AsyncTask 或者ThreadPoolExecutor或者其他形式自定义的线程池),不允许在应用中自行显式创建线程。
【强制】线程池不允许使用Executors 去创建,而是通过ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
为什么要这样规定:
(第一条)至于第一条前面解释很清楚了,因为显示的通过 Thread类,或者Runable,Callble接口创建的线程不好管理,而且无法复用。执行多条任务时会造成线程开启关闭的资源浪费。(第二条) 其实查看通过 Executors创建的四种常用线程池就可以发现,其中
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
} //此未复制过来的源码,可以看出并未对任务队列做出限制,默认值 Integer.MAX_VALUE,这会导致任务对象过多创建
// newFixedThreadPool 同理
//ScheduledThreadPoolExecutor 同理
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
} //可以看出最大线程值比较大,会造成线程资源占用过多
ThreadPoolExecutor 类的构造函数如下:
复制代码
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
ThreadPoolExecutor的参数说明
细心的会发现有关 池 的配置参数都大致一样,下面参数和数据库连接池参数基本相同。 就像所有网络连接无论什么协议中都会有 ip port keeplive timeout基本参数一样
corePoolSize:
核心线程数。在创建线程池之后,默认情况下线程池中并没有任何的线程,而是等待任务到来才创建线程去执行任务,当线程池中的线程数目达到 corePoolSize后,超出的线程会在设置的时间内回收掉,但是corePoolSize中的线程不会被回收。
int NUMBER_OF_CORES = Runtime.getRuntime().availableProcessors();
maximumPoolSize:
线程池中的最大线程数。表示线程池中最多可以创建多少个线程,它真正的作用是:当线程池中的线程数等于 corePoolSize 并且 workQueue 已满,这时就要看当前线程数是否大于 maximumPoolSize,如果小于maximumPoolSize 定义的值,则会继续创建线程去执行任务, 否则将会调用去相应的任务拒绝策略来拒绝这个任务。另外超过 corePoolSize的线程被称做"Idle Thread", 这部分线程会有一个最大空闲存活时间(keepAliveTime),如果超过这个空闲存活时间还没有任务被分配,则会将这部分线程进行回收。
keepAliveTime:
控制"idle Thread"的空闲存活时间。这个idle Thread就是上面提到的超过 corePoolSize 后新创建的那些线程,默认情况下,只有当线程池中的线程数大于corePoolSize,且这些"idle Thread"并没有被分配任务时,这个参数才会起作用。另外,如果调用了 ThreadPoolExecutor#allowCoreThreadTimeOut(boolean) 的方法,在线程池中的线程数不大于corePoolSize,且这些core Thread 也没有被分配任务时,keepAliveTime 参数也会起作用。
unit:
参数keepAliveTime的时间单位,共7种取值,在TimeUtil中定义:
TimeUnit.DAYS; //天
TimeUnit.HOURS; //小时
TimeUnit.MINUTES; //分钟
TimeUnit.SECONDS; //秒
TimeUnit.MILLISECONDS; //毫秒
TimeUnit.MICROSECONDS; //微妙
TimeUnit.NANOSECONDS; //纳秒
workQueue:
阻塞队列。如果当前线程池中的线程数目>=corePoolSize,则每来一个任务,会尝试将其添加到该队列当中,注意只要超过了 corePoolSize 就会把任务添加到该缓存队列,添加可能成功也可能不成功,如果成功的话就会等待空闲线程去执行该任务,若添加失败(一般是队列已满),就会根据当前线程池的状态决定如何处理该任务(若线程数 < maximumPoolSize 则新建线程;若线程数 >= maximumPoolSize,则会根据拒绝策略做具体处理)。
常用的阻塞队列有:
ArrayBlockingQueue //基于数组的先进先出队列,此队列创建时必须指定大小;
LinkedBlockingQueue //基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
SynchronousQueue //这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务。
threadFactory:
线程工厂。用来为线程池创建线程,当我们不指定线程工厂时,线程池内部会调用Executors.defaultThreadFactory()创建默认的线程工厂,其后续创建的线程优先级都是Thread.NORM_PRIORITY。其实就是指定了我也不清楚能干啥。
handler:
拒绝执行策略。当线程池的缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略:
ThreadPoolExecutor.AbortPolicy: // 丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy: // 也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy: // 丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy: // 由调用线程处理该任务
以上是关于java中的线程池的主要内容,如果未能解决你的问题,请参考以下文章
newCacheThreadPool()newFixedThreadPool()newScheduledThreadPool()newSingleThreadExecutor()自定义线程池(代码片段