概述
在编程规范中,不建议使用Executors去创建线程池,而是推荐使用ThreadPoolExecutor。
ThreadPoolExecutor会更明确运行规则,避免资源耗尽的风险。
因为Executors返回线程池有弊端:
1)FixedThreadPool和SingleThreadPool,允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool和ScheduledThreadPool,允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
构造方法
构造方法摘要 | |
---|---|
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) 用给定的初始参数和默认的线程工厂及被拒绝的执行处理程序创建新的 ThreadPoolExecutor。 |
|
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) 用给定的初始参数和默认的线程工厂创建新的 ThreadPoolExecutor。 |
|
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) 用给定的初始参数和默认被拒绝的执行处理程序创建新的 ThreadPoolExecutor。 |
|
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) 用给定的初始参数创建新的 ThreadPoolExecutor。 |
下面用一个例子来说明这个。
一个任务类,里面可以是具体的执行单元,这里仅是个示例:
public class Task implements Runnable{
private int i;
public Task(int i){this.i = i;}
@Override
public void run() {
try {
Thread.sleep(10000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadName:"+Thread.currentThread().getName()+"线程执行:"+i);
}
}
自定义ThreadFactory:
public class MyThreadFactory implements ThreadFactory {
/**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*/
@Override
public Thread newThread(Runnable r) {
return new Thread(r);
}
}
简单使用:
private static void simple(){
ThreadFactory namedThreadFactory = new MyThreadFactory();
int queueCapacity = 3, corePoolSize=2, maximumPoolSize=3;
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(queueCapacity);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,10, TimeUnit.SECONDS,arrayBlockingQueue,namedThreadFactory);
for(int i=1;i<5;i++){
Thread thread = namedThreadFactory.newThread(new Task(i));
threadPoolExecutor.execute(thread);
System.out.println("i:"+i+", queueSize:"+arrayBlockingQueue.size()
+", poolSize:"+threadPoolExecutor.getPoolSize()
+", coreSize:"+threadPoolExecutor.getCorePoolSize()
+", maxSize:"+threadPoolExecutor.getMaximumPoolSize());
}
threadPoolExecutor.shutdown();
while (true){
if(threadPoolExecutor.isTerminated()){
System.out.println("over");
break;
}
}
System.out.println( );
}
使用LinkedBlockingQueue作为示例队列
private static void LinkedBlockingQueue(){
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
int queueCapacity = 3, corePoolSize=2, maximumPoolSize=3;
LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue(queueCapacity);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,10, TimeUnit.SECONDS,linkedBlockingQueue,namedThreadFactory);
for(int i=1;i<21;i++){
Thread thread = namedThreadFactory.newThread(new Task(i));
System.out.println("即将添加数据:"+i);
threadPoolExecutor.execute(thread);
System.out.println("i:"+i+", queueSize:"+linkedBlockingQueue.size()
+", poolSize:"+threadPoolExecutor.getPoolSize()
+", coreSize:"+threadPoolExecutor.getCorePoolSize()
+", maxSize:"+threadPoolExecutor.getMaximumPoolSize());
}
看打印的日志:
即将添加数据:1
i:1, queueSize:0, poolSize:1, coreSize:2, maxSize:3
即将添加数据:2
i:2, queueSize:0, poolSize:2, coreSize:2, maxSize:3
即将添加数据:3
i:3, queueSize:1, poolSize:2, coreSize:2, maxSize:3
即将添加数据:4
i:4, queueSize:2, poolSize:2, coreSize:2, maxSize:3
即将添加数据:5
i:5, queueSize:3, poolSize:2, coreSize:2, maxSize:3
即将添加数据:6
i:6, queueSize:3, poolSize:3, coreSize:2, maxSize:3
即将添加数据:7
Exception in thread "main" java.util.concurrent.RejectedExecutionException
当添加第一个任务的时候,由于线程池空着,直接创建核心线程来处理请求;
当已经添加完两个请求,添加第三个请求的时候,核心线程数已满,则往队列里面添加,此时queueSize=1;
添加3、4、5任务,都被添加到了队列里面,此时queueSize=3;
添加6任务的时候,核心线程已满,队列已满,运行的线程数小于maximumPoolSize,那么线程池再处理一个任务,此时poolSize=3,线程池的任务满了;
添加7任务的时候,由于线程池里面的任务还没有执行完,而队列也是满的,线程池处理不了这么多任务了,抛出异常java.util.concurrent.RejectedExecutionException。
综上:先创建核心线程,够数后往队列里面塞,塞满继续创建执行线程,再满后抛出拒绝执行的异常。我们在执行的时候希望往池子里面一直扔,盛不下了也别抛异常,怎么办?
可以通过判断池子的大小,如果已经满了,则阻塞添加:
private static void LinkedBlockingQueue(){
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
int queueCapacity = 3, corePoolSize=2, maximumPoolSize=3;
LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue(queueCapacity);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,10, TimeUnit.SECONDS,linkedBlockingQueue,namedThreadFactory);
for(int i=1;i<21;i++){
Thread thread = namedThreadFactory.newThread(new Task(i));
System.out.println("即将添加数据:"+i);
threadPoolExecutor.execute(thread);
System.out.println("i:"+i+", queueSize:"+linkedBlockingQueue.size()
+", poolSize:"+threadPoolExecutor.getPoolSize()
+", coreSize:"+threadPoolExecutor.getCorePoolSize()
+", maxSize:"+threadPoolExecutor.getMaximumPoolSize());
while((threadPoolExecutor.getPoolSize()+linkedBlockingQueue.size())==(queueCapacity + maximumPoolSize)){
System.out.println("线程池已满,休眠等待 i:"+i+", queueSize:"+linkedBlockingQueue.size()
+", poolSize:"+threadPoolExecutor.getPoolSize()
+", coreSize:"+threadPoolExecutor.getCorePoolSize()
+", maxSize:"+threadPoolExecutor.getMaximumPoolSize());
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
threadPoolExecutor.shutdown();
while (true){
if(threadPoolExecutor.isTerminated()){
System.out.println("run over");
break;
}
}
}
在for循环里面进行了判断:
(threadPoolExecutor.getPoolSize()+linkedBlockingQueue.size())==(queueCapacity + maximumPoolSize),表示当前队列里面的线程数加上线程池里面当前线程数等于当前线程池可处理的最大线程的时候,进行Thread.sleep(1000L)等待,知道线程池有空闲资源的时候继续执行添加操作。
避免异常,可以使用阻塞队列ArrayBlockingQueue
private static void ArrayBlockingQueue(){
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
int queueCapacity = 3, corePoolSize=2, maximumPoolSize=3;
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(queueCapacity);
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,10, TimeUnit.SECONDS,arrayBlockingQueue,namedThreadFactory);
for(int i=1;i<21;i++){
int finalI = i;
if((threadPoolExecutor.getPoolSize()+arrayBlockingQueue.size())>=(queueCapacity+maximumPoolSize)){
try {
Thread thread = namedThreadFactory.newThread(new Task(finalI));
arrayBlockingQueue.put(thread);
System.out.println("队列中添加线程 i:"+i+", queueSize:"+arrayBlockingQueue.size()
+", poolSize:"+threadPoolExecutor.getPoolSize()
+", coreSize:"+threadPoolExecutor.getCorePoolSize()
+", maxSize:"+threadPoolExecutor.getMaximumPoolSize());
} catch (InterruptedException e) {
e.printStackTrace();
}
}else {
Thread thread = namedThreadFactory.newThread(new Task(finalI));
threadPoolExecutor.execute(thread);
System.out.println("i:"+i+", queueSize:"+arrayBlockingQueue.size()
+", poolSize:"+threadPoolExecutor.getPoolSize()
+", coreSize:"+threadPoolExecutor.getCorePoolSize()
+", maxSize:"+threadPoolExecutor.getMaximumPoolSize());
}
}
threadPoolExecutor.shutdown();
while (true){
if(threadPoolExecutor.isTerminated()){
System.out.println("over");
break;
}
}
System.out.println( );
}
使用ArrayBlockingQueue 阻塞机制,来实现同Thread.sleep()同样的效果。