ThreadPoolExecutor是 java提供的一个线程池工具类
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
maximumPoolSize 最大线程数
keepAliveTime 线程最大空闲时间 (当线程数大于 corePoolSize时,剩余的线程的空闲时间大于keepAliveTime将会被回收
unit keepAliveTime对应的时间单位
workQueue 任务队列
threadFactory 线程工厂
handler (饱和策略) 当线程数达到最大,且任务队列已满,就会调用 此handler的方法处理
默认情况下 不传入 threadFactory会提供一个默认的值,Executors.defaultThreadFactory()
不过默认的线程池名字是很揪心的,格式都是 pool-poolNumber-thread-threadNumber
所以要是创建了好几个线程池,名字基本就是pool-1-thread-1,pool-1-thread-2, pool-2-thread-1, pool-2-thread-2,完全没有可读性
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
google 的guava提供了一个方法设置线程池名称 new ThreadFactoryBuilder().setNameFormat("scheduler-pool-%d").build();
这样一看就知道是什么线程了
如果不想引入google的guava,就自己写个类继承ThreadFactory,
public class MqThreadFactory implements ThreadFactory {
private String poolName;
private final ThreadGroup group;
private final AtomicInteger threadNumber = new AtomicInteger(1);
private final String namePrefix;
public MqAppThreadFactory(String poolName) {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() : Thread.currentThread().getThreadGroup();
namePrefix = poolName + "-pool" + "-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(), 0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
调用的时候传入poolName就行了
java提供了几种饱和策略供选择
AbortPolicy 当线程数达到最大,且任务队列已满,直接抛出异常,这也是ThreadPoolExecutor的默认饱和策略
DiscardPolicy 直接丢弃任务
DiscardOldestPolicy 丢弃队列里最近的一个任务,并执行当前任务
CallerRunsPolicy 只用调用者所在线程来运行任务
threadPoolExecutor使用方式:
ExecutorService pool=new ThreadPoolExecutor(coreThreadSize, maxThreadSize,keepAliveTime, unit,
queue, new MqThreadFactory(poolName)
这里使用默认的饱和策略
然后提交任务到线程池就行了
executorService.execute(new QueueHandler());
当然也可以调用 executorservice.submit 方法
提交到线程池的任务并不一定会马上执行,之前碰到一个问题,线程的代码默认监听BlockingQueue,
希望提高性能,所以项目初始化的时候,循环调用execute方法,希望线程池中的所有线程默认都开启,
结果发现,线程数维持在,corePoolSize,
下面是execute方法的执行流程:
1、线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作
线程来执行任务。如果核心线程池里的线程都在执行任务,则进入下个流程。
2、线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这
个工作队列里。如果工作队列满了,则进入下个流程。
3、线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程
来执行任务。如果已经满了,则交给饱和策略来处理这个任务
具体的大家可以看看 java并发编程的艺术 这本书,个人觉得在某些细节上面讲的比 java并发编程实战要好