常用的创建线程池的几种方式。
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
固定大小的线程池,传入的参数为核心线程的数量,使用这种方式代表创建一个核心线程和最大线程相等的线程池,keepAliveTime的设定时间为0,线程的存活时间是无限的,除非调用了shutdownnow方法,否则创建的线程不会被回收,创建的线程达到核心线程数的时候,新来的任务放入LinkedBlockingQueue队列,它有着更高的吞吐量,在并发比较高的情况下,内部采取了两把ReentrantLock来控制,生产者和消费者可以并行地操作队列。
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
单一线程池,核心线程和最大线程数都为1,只创建一个线程来执行任务,保证所有的任务按照FIFO的顺序执行,这个类还用FinalizableDelegatedExecutorService进行了包装,主要还是为了防止动态地修改线程的数量,虽然只有一个线程,一旦线程因为异常中止的时候,ThreadPoolExecutor会在创建一个线程继续工作。如果并发比较高的场景,LinkedBlockingQueue上放入的任务过多,势必会影响内存。本质上还是使用于单线程处理任务的场景。
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
核心线程数为0,最大线程数为Integer的最大值,每当产生一个新的任务,就会放入SynchronousQueue队列,入队的任务需要一直等待直到队列中的任务被消费,keepAliveTime为60秒,如果在此时间内线程从队列中取不到任务就要被回收。在并发情景中,第一个任务会立即放入queue队列,创建一个线程去queue中取任务执行,其他的任务就会创建新的线程来执行,线程的数量会随着任务的数量变化。这个方式更适合于执行短期异步的任务。
newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
new DelayedWorkQueue());
}
创建一个定时或周期性执行任务的线程池,创建的核心线程不会被回收,如果创建的线程超过了最大线程数,就会把新来的任务放入DelayedWorkQueue队列,这是一种按照超时时间排序的队列结构。更适合于周期性的执行任务。
Java8又加入了一种newWorkStealingPool线程池,不过暂时还没有研究。
一般在项目中不会直接使用这几种方式创建线程池,而是使用ThreadPoolExecutor,它可以自定义所需的参数。比如创建ThreadPoolExecutor
ThreadPoolExecutor tpe = new ThreadPoolExecutor(5,10,60,TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(10));
我们可以重写ThreadFactory,修改线程名,设置守护线程,线程的优先级,最好重新命名一下线程池,这样如果项目中出了问题方便排查。
public static class ReThreadFactory 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;
ReThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "Mypools-" + "GWMthreadName-";
}
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
t.setDaemon(false);
t.setPriority(Thread.MAX_PRIORITY);
return t;
}
}
我们也可以自定义任务的处理策略。
static public class MyRejectedExecutionHandler implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 处理任务被拒绝的情况,比如写入日志等
}
}
ThreadPoolExecutor有一些未实现的钩子方法,我们可以继承ThreadPoolExecutor,重写这些方法,已达到我们的目的。
我们也可以重写线程池来为我们所用。
public class TaskBlockingQueue extends LinkedBlockingQueue<Runnable> {
/**
*
*/
private static final long serialVersionUID = 1L;
private MyThreadPoolExecutor executor;
public TaskBlockingQueue(int capacity) {
super(capacity);
}
public void setExecutor(MyThreadPoolExecutor exec) {
executor = exec;
}
public boolean forceTaskIntoQueue(Runnable o) {
if (executor.isShutdown()) {
throw new RejectedExecutionException("队列已满");
}
return super.offer(o);
}
@Override
public boolean offer(Runnable o) {
int currentPoolThreadSize = executor.getPoolSize();
// 线程数>最大线程数
if (currentPoolThreadSize >= executor.getMaximumPoolSize()) {
return super.offer(o);
}
// 小于核心线程加入队列
if (executor.getSubmittedTaskCount() < currentPoolThreadSize) {
return super.offer(o);
}
// 线程数<最大线程数
if (currentPoolThreadSize < executor.getMaximumPoolSize()) {
return false;
}
return super.offer(o);
}
}
public class MyThreadPoolExecutor extends ThreadPoolExecutor {
private final AtomicInteger submittedTaskCount = new AtomicInteger(0);
public MyThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, TaskBlockingQueue workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new ThreadPoolExecutor.AbortPolicy());
workQueue.setExecutor(this);
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
submittedTaskCount.decrementAndGet();
}
public int getSubmittedTaskCount() {
return submittedTaskCount.get();
}
@Override
public void execute(Runnable command) {
submittedTaskCount.incrementAndGet();
try {
super.execute(command);
} catch (RejectedExecutionException rx) {
BlockingQueue<Runnable> taskQueue = super.getQueue();
if (taskQueue instanceof TaskBlockingQueue) {
final TaskBlockingQueue queue = (TaskBlockingQueue)taskQueue;
if (!queue.forceTaskIntoQueue(command)) {
submittedTaskCount.decrementAndGet();
throw new RejectedExecutionException("队列已满");
}
} else {
submittedTaskCount.decrementAndGet();
throw rx;
}
}
}
}
用代码来测试一下,效果如何,,,
public static void main(String[] args){
int coreSize = 5;
int maxSize = 10;
long keepAliveTime = 10;
int queueSize = 5;
MyThreadPoolExecutor executor = new MyThreadPoolExecutor(coreSize,maxSize,keepAliveTime, TimeUnit.SECONDS , new TaskBlockingQueue(queueSize));
for (int i = 0; i < 15; i++) {
executor.execute(new Runnable() {
@SuppressWarnings("static-access")
@Override
public void run() {
try {
Thread.currentThread().sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
System.out.println("线程池中线程数是:"+ executor.getPoolSize()+",队列中任务数是:"+ executor.getQueue().size());
}
}
结果如下,说明重写的线程池符合预期。
线程池中线程数是:1,队列中任务数是:0
线程池中线程数是:2,队列中任务数是:0
线程池中线程数是:3,队列中任务数是:0
线程池中线程数是:4,队列中任务数是:0
线程池中线程数是:5,队列中任务数是:0
线程池中线程数是:6,队列中任务数是:0
线程池中线程数是:7,队列中任务数是:0
线程池中线程数是:8,队列中任务数是:0
线程池中线程数是:9,队列中任务数是:0
线程池中线程数是:10,队列中任务数是:0
线程池中线程数是:10,队列中任务数是:1
线程池中线程数是:10,队列中任务数是:2
线程池中线程数是:10,队列中任务数是:3
线程池中线程数是:10,队列中任务数是:4
线程池中线程数是:10,队列中任务数是:5
我们可以把offer的代码注释掉,对比一下。。
线程池中线程数是:1,队列中任务数是:0
线程池中线程数是:2,队列中任务数是:0
线程池中线程数是:3,队列中任务数是:0
线程池中线程数是:4,队列中任务数是:0
线程池中线程数是:5,队列中任务数是:0
线程池中线程数是:5,队列中任务数是:1
线程池中线程数是:5,队列中任务数是:2
线程池中线程数是:5,队列中任务数是:3
线程池中线程数是:5,队列中任务数是:4
线程池中线程数是:5,队列中任务数是:5
线程池中线程数是:6,队列中任务数是:5
线程池中线程数是:7,队列中任务数是:5
线程池中线程数是:8,队列中任务数是:5
线程池中线程数是:9,队列中任务数是:5
线程池中线程数是:10,队列中任务数是:5
线程池还是非常重要的,了解其原理,才能更好的为我们所用。所以在工作或学习中更要注重基础知识的积累,只有知其然知其所以然,才能用我们的能力更好地服务于项目。
参照tomcat中ThreadPoolExecutor的源码。