ThreadPoolExecutor
BlockingQueue
先来说3种BlockingQueue
- SynchronousQueue。队列长度始终为0,因为任何一个put操作必须等待一个take操作,否则put就一直阻塞。所以一旦put成功之后,元素马上就被take出去了,队列长度始终为0。
- ArrayBlockingQueue。基于定长数组的队列。生产者和消费者使用的锁不是隔离的,即take和put共用同一个锁。
- LinkedBloackingQueue。不指定容量时其容量为Integer.MAX,有耗尽内存的风险。指定capacity后,如果size达到capacity,则继续进行put操作会阻塞,这一点跟普通的java collection是不一样的,普通的collection即使指定了容量,当size达到capacity后也会自动扩容。生产者和消费者使用的锁是隔离的。
ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
当向ThreadPoolExecutor提交任务时存在以下几种情况:
- 线程池中的线程数没有达到corePoolSize,则直接创建新的线程执行任务,并把该线程放到线程池中。
- 线程池中的线程数达到了corePoolSize,同时workQueue队列还没满,则把任务放到workQueue中去,等待被执行。线程池中如果有某个线程空闲下来,它会去workQueue中take()一个任务来执行,如果workQueue已空,则take()操作会阻塞。
- 如果线程池中的线程数达到corePoolSize了,且workQueue也满了,则创建新的线程执行任务,并把该线程放到线程池中。此时线程池中的线程数已经开始超过corePoolSize了,线程池中超过corePoolSize的那部分线程,当它空闲下来且空闲时间达到keepAliveTime(时间单位由unit指定)后线程自行销毁。线程空闲下来后为什么不直接销毁,而是要先keep一段时间?因为频繁地创建销毁线程是很消耗CPU资源的,好不容易从CPU那儿借来一个线程,就索性多借一会儿,说不定刚空闲一小会儿就又有新任务需要执行呢。
- 线程池数目达到了maximumPoolSize,则采用handler指定的策略丢弃任务。
注意:如果workQueue使用的是没有指定容量的LinkedBloackingQueue,则workQueue永远不会满,线程池的线程数永远不会超过corePoolSize,此时指定maximumPoolSize、keepAliveTime、handler都是没有意义的。
public interface ThreadFactory {
Thread newThread(Runnable r);
}
自己实现的ThreadFactory,在newThread()方法中可以自定义Thread的一些属性,比如线程优化先、名称、是否为后台线程、线程所属的组、线程对异常的处理方式等等。看一下Executors类中的DefaultThreadFactory是怎么写的:
static class DefaultThreadFactory implements ThreadFactory {
static final AtomicInteger poolNumber = new AtomicInteger(1);
final ThreadGroup group;
final AtomicInteger threadNumber = new AtomicInteger(1);
final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null)? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-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;
}
}
RejectedExecutionHandler有这么几种:
-
ThreadPoolExecutor.AbortPolicy():抛出java.util .concurrent.RejectedExecutionException异常。这是ThreadPoolExecutor默认采用的拒绝策略。
- ThreadPoolExecutor.CallerRunsPolicy():重试添加当前的任务,他会自动重复调用execute()方法
- ThreadPoolExecutor.DiscardOldestPolicy():抛弃旧的任务
- ThreadPoolExecutor.DiscardPolicy():抛弃当前的任务
用ThreadPoolExecutor构建ExecutorService
java中几种常见的ExecutorService都是通过ThreadPoolExecutor来实现的。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
使用的是无界队列LinkedBlockingQueue,所以线程池中的线程数始终不会超过nThreads。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
SingleThreadExecutor是FixedThreadPool的特例,把nThreads改为1即可。
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
使用了SynchronousQueue,所以一旦提交一个任务马上就会创建一个线程去执行它,这种线程的keepAlive时间较长,是60秒。又因为corePoolSize为0,所以当确实没有什么任务需要执行时,线程池中的线程数就是0。这一点跟其他ThreadPoolExecutor是不一样的,其他ThreadPoolExecutor当线程池中的数目达到corePoolSize后就再也降不到corePoolSize以下了,因为就算无任务可做这部分线程会因为在workQueue上调用了take()操作而阻塞,但这部分线程不会被销毁。
本文来自博客园,作者:张朝阳讲go语言,转载请注明原文链接:https://www.cnblogs.com/zhangchaoyang/articles/4340803.html

浙公网安备 33010602011771号