线程池解决了2个不同的问题
1.提升了性能:通常是在执行大量的执行任务时,减少了每个线程的调用开销,并且它们提供了一种限制和资源管理(包括线程)的方法,从而提升了性能。
2.统计信息:每个TheardPoolExecutor保持一些基本的统计信息,例如完成的任务数量。
Executors.newCachedThreadPool(无界线程池,自动线程回收);
Executors.newFixedThreadPool(固定大小的线程池);
Executors.newSingleThreadExecutor(后台单一线程);
而线程池不允许用Executors去创建,而需要通过ThreadPoolExecutor方式,这一方面是由于jdk 中 Executor框架虽然提供了newFixedThreadPool(),newCacheThreadPool(),newSingleThreadExecutor() 等创建线程池的方法,但是都有局限性,不够灵活,而且前面的这几种方法内部也是通过ThreadPoolExecutor 方式实现,使用ThraedPoolExecutor有助于大家明确线程池的运行规则,创建符合符合自己业务场景需要的线程池,避免资源耗尽的风险。
ThreadPoolExector构造函数
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
构造函数的参数含义如下:
corePoolSize:指定了线程池中线程的数量,它的数量决定了添加的任务是开辟新的线程去执行,还是放到workQueue中去执行;
maximumPoolSize:指定了线程池中的最大线程数量,这个参数会根据你使用的workQueue任务队列的类型,决定线程池会开辟的最大线程数量;
keepAliveTime:当线程池中空闲线程数量超过corePoolSize,多余的线程会在多长时间内被销毁;
unit:keppAliveTime的单位;
workQueu:任务队列,被添加到线程池中,但尚未被执行的任务;他一般分为直接提交队列,有界任务队列,无界任务队列,优先任务队列;
threadFactory:线程工厂,用于创建线程,一般使用默认就可以了;
handler:拒绝策略;当任务太多来不及处理时,如何拒绝任务;
1.添加任务到线程池
通过execute(Runable)方法被添加到线程池,任务就是一个Runable类型的对象,执行任务的方法就是Runable类型对象的run()方法。当一个任务通过execute(Runnable)方法欲添加到线程池时:
如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也会创建新的线程来处理被添加的任务;
如果此时线程池中的数量等于corePoolSize,但是缓冲队列workQueue未满,那么任务会被放入缓冲队列中;
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,创建新的线程来处理被添加的任务;
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过handler所指定的策略来处理任务;
也就是:处理任务的优先级为:
核心线程corePoolSize,任务队列workQueue,最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
当线程池中的数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,该线程将会被终止。这样,线程池也可以动态调整线程池中的线程数量;
unit可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS。
workQueue常用的是:java.util.concurrent.BlockingQueue
从中可以发现ThreadPoolExecutor是依靠BlockingQueue的阻塞机制来维持线程池,当池子里的线程无事可干的时候就通过workQueue.take()阻塞住。
handler有4个选择:
ThreadPoolExecutor.Abortpolicy(); 抛出java.util.concurrent.RejectedExecutionException异常;
ThreadPoolExecutor.CallerPolicy(); 重试添加当前任务,他会自动重复调用execute()方法;
ThreadPoolExecutor.DiscardOldestPolicy(); 抛弃旧的任务;
ThreadPoolExecutor.DiscardPolicy(); 抛弃当前任务;
2.线程池的使用场合
(1)单个任务处理的时间比较短;
(2)需要处理的任务数量大;
应用举例
package demo;
import java.io.Serializable;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorDemo {
private static int produceTaskSleepTime = 5;
private static int consumeTaskSleepTime = 5000;
private static int produceTaskMaxNumber = 20; // 定义最大添加20个线程到线程池中
public static void main(String[] args) {
// 构造一个线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 4, 3, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(3), new ThreadPoolExecutor.DiscardOldestPolicy());
for (int i = 1; i <= produceTaskMaxNumber; i++) {
try {
// 一个任务,并将其加入到线程池
String work = "work@" + i;
System.out.println("put:" + work);
threadPool.execute(new ThreadPoolTask(work));
// 便于观察,等待一段时间
Thread.sleep(produceTaskSleepTime);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public static class ThreadPoolTask implements Runnable, Serializable {
private static final long serialVersionUID = 0;
// 保存任务所需要的数据
private Object threadPoolTaskData;
ThreadPoolTask(String work) {
this.threadPoolTaskData = work;
}
public void run() {
// 处理一个任务,这里的处理方式太简单了,仅仅是一个打印语句
System.out.println("start-----" + threadPoolTaskData);
try {
// 便于观察,等待一段时间
Thread.sleep(consumeTaskSleepTime);
} catch (Exception e) {
e.printStackTrace();
}
threadPoolTaskData = null;
}
public Object getTask() {
return this.threadPoolTaskData;
}
}
}