ThreadPoolExecutor是java.util.concurrent包提供的基础线程池,使用非常广泛
让我们来看一下线程池的使用和内部实现原理
下面是ThreadPoolExecutor的一个构造方法,最终所有其他构造方法都要调用这个构造方法,来看一下构造方法中的参数的作用
corePoolSize:核心线程池的大小,当提交一个任务到线程池时,线程池会创建一个线程来执行任务,即使其他空闲的基本线程能够执行新任务也会创建线程,等到需要执行的任务数大于线程池基本大小时就不再创建。调用perstartAllCoreThreads()方法,线程池会提前创建并启动所有基本线程
maximumPoolSize:线程池允许创建的最大线程数。如果队列满了,并且已经创建的线程数小于最大线程数,则线程池会再创建新的线程执行任务。如果是无界队列这个参数不起作用
keepAliveTime:当线程池中的线程数大于corePoolSize时,keepAliveTime为多余的空闲线程等待新任务的最长时间,超过这个时间后多余的线程将被终止。这里把keepAliveTime设置为0L,意味着多余的空闲线程会被立即终止。
unit:时间单元
workQueue:线程池的工作队列,工作队列最常用的有如下几种
ArrayBlockingQueue:是一个基于数组结构的游街阻塞队列
LinkedBlockingQueue:基于链表结构的阻塞队列
SynchronousQueue:不存储元素的阻塞队列,插入元素后必须等待另一个线程调用移除操作
threadFactory:创建线程的工厂
handler:饱和策略。当队列和线程都满了,必须采取一种策略处理提交的新任务。默认策略是AbortPolicy,JDK1.5提供了如下4种策略,除了这四种策略外还可以自己来实现RejectedExecutionHandler接口来实现自定义的策略
AbortPolicy:直接抛出异常
CallerRunsPolicy:只用调用者所在线程来运行任务
DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务
DiscardPolicy:不处理,丢弃掉
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue 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;
}
线程池的实现原理
当向线程池提交一个任务之后,线程池的处理流程如下:
1)线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务(使用threadFactory来创建)。如果核心线程池里的线程都在执行任务,则进入下个流程
2)线程池判断工作队列是否已经满了。如果工作队列没有满,则将新提交的任务存储在这个工作队列里(对应构造函数中的workQueue),则将新提交的任务存储在这个工作队列里。如果工作队列满了,则进入下个流程
3)线程池判断线程池是否已经满了(线程达到了最大数,且任务队列都满了)。如果没有,创建一个新的工作线程来执行任务。如果已经满了,任务将被拒绝并交给饱和策略来处理这个任务
线程池创建好了之后就可以向线程池里提交任务了
向线程池提交任务有两种方法,分别是execute()和submit()方法
区别是execute()方法用于提交没有返回值的任务,submit()方法用于提交需要返回值的任务。线程池会返回一个Future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,还可以使用重载的get()方法阻塞一段时间后返回不管任务是否执行完
public class ThreadPoolExecutorDemo {
public static void main(String[] args) {
ThreadPoolExecutor tpe =
new ThreadPoolExecutor(1, 1, 1, TimeUnit.SECONDS, new LinkedBlockingDeque());
tpe.execute(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("执行run方法");
}
});
Future future =tpe.submit(new Callable() {
@Override
public String call() throws Exception {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("执行call方法");
return "success";
}
});
for(int i=0;i<5;i++){
String str=null;
try {
str = future.get(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TimeoutException e) {
System.err.println("超时未返回");
}
System.out.println("get方法返回值为:"+str);
}
}
}
ps:如果需要了解并发相关的知识建议以下两本书《JAVA并发编程实践》,《JAVA并发编程的艺术》。