为何要使用线程池建立线程?
使用线程池的好处是减小在建立和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。若是不使用线程池,有可能形成系统建立大量同类线程而致使消耗完内存或者“过分切换”的问题。
为何线程池不容许使用 Executors 去建立,而是经过 ThreadPoolExecutor 的方式?
Executors 返回的线程池对象的弊端以下:
1) FixedThreadPool 和 SingleThreadPool :
容许的请求队列长度为 Integer.MAX_VALUE ,可能会堆积大量的请求,从而致使 OOM 。
2) CachedThreadPool 和 ScheduledThreadPool :
容许的建立线程数量为 Integer.MAX_VALUE ,可能会建立大量的线程,从而致使 OOM 。
以上内容摘自阿里巴巴Java开发手册-编程规约-并发处理java
下面用一个简单的例子演示使用ThreadPoolExecutor建立一个线程池编程
public static void main(String[] args){ //建立线程工厂 ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("test-pool-%d").build(); //建立线程池 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(1024), threadFactory, new ThreadPoolExecutor.AbortPolicy()); //执行10个线程 for(int i=0;i<10;i++) { threadPool.execute(() -> { System.out.println(Thread.currentThread().getName()); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } }); } //关闭线程池,不在接受新任务 threadPool.shutdown(); }
咱们先看ThreadPoolExecutor的构造方法,ThreadPoolExecutor提供了三个构造方法,咱们看参数最全的这个:并发
/** * 参数意义: * corePoolSize:核心线程数量,线程池中常存的线程数量,即便这些线程是空闲状态; * 但若是咱们设置了allowCoreThreadTimeOut这个参数为true, * 这时核心线程将在空闲超过keepAliveTime后被终止 * maximumPoolSize:最大线程数量,即线程池中容许的最大线程数 * keepAliveTime:多余线程存活时间,当池中线程数大于核心数时,多余空闲线程处于空闲状态的时间不会超过这个时间, * 即多余的空闲线程超过这个时间就会被终止 * unit:keepAliveTime参数的时间单位 * workQueue:任务队列,BlockingQueue类型,该队列用来保存execute方法提交的Runnable任务; * 若是池中没有空闲的线程是,新任务将会保存到这个队列中,等待有空闲的线程后再执行。 * threadFactory:线程工厂,用于线程池中建立新线程 * handler:若是线程数量已经最大、任务队列已经满了,拒绝新任务的处理策略 */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, java.util.concurrent.TimeUnit unit, java.util.concurrent.BlockingQueue<Runnable> workQueue, java.util.concurrent.ThreadFactory threadFactory, java.util.concurrent.RejectedExecutionHandler handler)
下面咱们对workQueue、threadFactory、handler三个参数详细介绍:
一、workQueue:BlockingQueue类型参数一个阻塞队列,主要用于缓冲任务;ui
关于阻塞队列能够查看个人另外一篇文章:Java阻塞队列—BlockingQueuegoogle
二、threadFactory:ThreadFactory类型参数,用来告诉线程池怎么来建立线程
ThreadFactory接口中定义了方法 Thread newThread(Runnable r);经过实现该接口咱们能够定义接口的名称、优先级、是否为守护线程等,咱们能够本身实现也能够采用第三方的实现。
咱们参考一下JDK中 Executors类中的DefaultThreadFactory实现,DefaultThreadFactory的源码以下: .net
static class DefaultThreadFactory implements ThreadFactory { private static final AtomicInteger poolNumber = new AtomicInteger(1); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; DefaultThreadFactory() { SecurityManager s = System.getSecurityManager(); //设置线程组同当前线程一致 group = (s != null) ? s.getThreadGroup() :Thread.currentThread().getThreadGroup(); //线程名称前缀 namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-"; } public Thread newThread(Runnable r) { Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0); //设为非守护线程 if (t.isDaemon()) t.setDaemon(false); //线程的优先级设为默认的5 if (t.getPriority() != Thread.NORM_PRIORITY) t.setPriority(Thread.NORM_PRIORITY); return t; } }
咱们使用Executors建立的FixedThreadPool、SingleThreadPool、 CachedThreadPool 和 ScheduledThreadPool就是采用的这个DefaultThreadFactory类。
咱们也可用第三方实现的线程建立工厂,如google的ThreadFactoryBuilder线程
ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("request-pool-%d") .setPriority(1) .build();
三、 handler:RejectedExecutionHandler类型参数,线程池拒绝新任务的处理策略
在ThreadPoolExecutor中有四个RejectedExecutionHandler的实现,分别是:code
ThreadPoolExecutor经常使用的方法:orm
//让线程池执行指定的任务,不必定是当即执行 void execute(Runnable command)
//向线程池中提交指定的任务,若是任务执行成功,则返回的Future对象get获得的将为null Future<?> submit(Runnable task)
//向线程池中提交指定的任务,若是任务执行成功,返回的Future对象get获得的为任务的返回值 <T> Future<T> submit(Callable<T> task)
//向线程池中提交指定的任务,若是任务执行成功,返回的Future对象get获得的就是入参result <T> Future<T> submit(Runnable task, T result)
//关闭线程池;该方法不会当即终止已经提交的任务,执行该方法后线程池将再也不接收新任务,待池中的全部任务执行完成后才会真正的关闭线程池 void shutdown()
//阻塞主线程指定的时间;若是池中任务所有完成返回true阻塞终止,若是超时返回false阻塞终止,主线程若是被中断抛出异常阻塞终止 boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException