1. 说明
最近笔者在开发时,遇到了这样的问题。当笔者通过WEB界面删除某一条记录时,系统老是报“系统出错”,并且想到删除的数据并没有删除掉。查看Tomcat的日志,发现报了以下的错误:
java.util.concurrent.RejectedExecutionException
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.reject(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor.execute(Unknown Source)
如果对线程熟悉的读者,一眼就会知道,这样的错是跟线程有关系的,也就是说我在执行一条删除记录时,该删除代码被线程拒绝执行了。那就其了怪了,为什么好好的,线程会拒绝执行我的代码呢?通过跟踪代码,笔者发现,我们系统对于线程的处理都是难过线程池的调用的,也就是说当前的处理可能是超出线程池的大小,所以才被拒绝执行。好了,笔者知道了问题所在,修改了线程池的大小,果真问题解决了。但为了进行一步了解线程池,今天笔者就总结了一下线程池的用法。
2. 介绍ThreadPoolExecutor
通过上面的报错信息,我们也就知道,线程池的类就是java.util.concurrent.ThreadPoolExecutor.通过源代码,我们可以看到这个方法的函数如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
我们可以看到,总共有六个参数,那么这几个对数的作用如何呢?笔者通过下面的表格来说明:
这几个参数的作用,网上还给出了以下几点说明:
1) 如果线程池中运行的线程 小于corePoolSize ,即使线程池中的线程都处于空闲状态,也要 创建新的线程 来处理被添加的任务。
2) 如果线程池中运行的线程大于等于corePoolSize,但是缓冲队列 workQueue未满 ,那么任务被放入缓冲队列 。
3) 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满(即无法将请求加入队列 ),并且线程池中的数量小于maximumPoolSize,建新的线程 来处理被添加的任务。
4) 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize ,那么通过 handler 所指定的策略来处理此任务。
5) 当线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止 。这样,线程池可以动态的调整池中的线程数。
也就是说处理任务的优先级为:corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
在使用线程池时,经常会抛出这两个错误:IllegalArgumentException、NullPointerException。那么在什么情况下抛出IllegalArgumentException,又在什么情况下抛出NullPointerException错误呢?下面表格给出说明:
3. 例子说明
1) 首先,创建一个通用的线程池。这里主要就是new一个ThreadPoolExexutor的类,对于线程拒绝处理任务的处理,我们给出了两个方案,分别是:使用有界队列策略和使用无界队列策略。无界队列,我们使用LinkedBlockingQueue;有界队列是ArrayBlockingQueue.这里需要说明的是,使用无界queuw可能会耗尽系统资源,使用有界queue可能不能很好的满足性能,需要调用线程数和queue大小。
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 创建线程池
*
* @author owenwilliam
* @Date 2017-4-17
*
*/
public class ThreadPool
{
public static final int ARRAY_QUEUE = 0; //ArrayBlockingQueue 有界队列策略
public static final int LINKED_QUEUE = 1; //LinkedBlockingQueue 使用无界队列策略
private ThreadPoolExecutor executor;
private BlockingQueue<Runnable> workQueue;
/**
* @param workQueneSize
* 队列长度
* @param coreSize
* 主线程数
* @param maxSize
* 最大线程数
* @param queueType
* 队列类型
*/
public ThreadPool(final int workQueneSize, final int coreSize,
final int maxSize, int queueType)
{
this(workQueneSize, coreSize, maxSize, queueType, null);
}
/**
*
* @param workQueneSize
* 队列长度
* @param coreSize
* 主线程数
* @param maxSize
* 最大线程数
* @param queueType
* 队列类型
* @param policy
* 处理策略
*/
public ThreadPool(final int workQueneSize, final int coreSize,
final int maxSize, int queueType, RejectedExecutionHandler policy)
{
workQueue = createQueue(queueType, workQueneSize);
executor = new ThreadPoolExecutor(coreSize, maxSize, 60,
TimeUnit.SECONDS, workQueue, policy != null ? policy
: new ThreadPoolExecutor.AbortPolicy());
}
public void execute(Runnable runnable)
{
if (workQueue.size() > 4)
{
System.out.println("当前等待线程大小:'"+runnable.getClass().getSimpleName()+
"':"+workQueue.size());
}
executor.execute(runnable);
}
/**
* 创建队列,选择不同和队列策略
* ArrayBlockingQueue 有界队列策略
* LinkedBlockingQueue 使用无界队列策略
* @param queueType
* @param queueSize
* @return
*/
private BlockingQueue<Runnable> createQueue(int queueType, int queueSize)
{
return queueType == LINKED_QUEUE ? new LinkedBlockingQueue<Runnable>(
queueSize) : new ArrayBlockingQueue<Runnable>(queueSize);
}
public BlockingQueue<Runnable> getQueue()
{
return executor.getQueue();
}
}
2) 创建一个线程。
/**
* 单个线程
* @author owenwilliam
* @Date 2017-4-17
*
*/
public class ThreadRunnable implements Runnable
{
String name;
public ThreadRunnable(String name)
{
this.name = name;
}
@Override
public void run()
{
// 处理一个任务,这里的处理方式太简单了,仅仅是一个打印语句
System.out.println("start .." + name);
try
{
// 便于观察,等待一段时间
Thread.sleep(2000);
} catch (Exception e)
{
e.printStackTrace();
}
}
}
3) 创建一个类调用线程池和线程。
/**
* 使用线程池
*
* @author owenwilliam
* @Date 2017-4-17
*
*/
public class USThreadPool
{
private ThreadPool tp;
private static USThreadPool pool = new USThreadPool();
private USThreadPool()
{
int workQueneSize = 80;
int coreSize = 4;
int maxSize = 10;
//创建线程池
tp = new ThreadPool(workQueneSize, coreSize, maxSize,
ThreadPool.ARRAY_QUEUE);
}
public static USThreadPool getPool()
{
return pool;
}
public void execute(Runnable runnable)
{
tp.execute(runnable);
}
public void execute(String name)
{
execute(new ThreadRunnable(name));
}
}
4) 最后写一个测试类。
/**
* 测试线程
*
* @author owenwilliam
* @Date 2017-4-17
*
*/
public class TestThread
{
public static void main(String[] args)
{
for (int i = 0; i < 10; i++)
{
USThreadPool.getPool().execute("num:" + i);
}
}
}
4. 执行结果
源码地址:git@github.com:owenwilliam/ThreadPool.git