这两天被线程池给整晕了,今天去找jh给我划一下Java核心技术里哪些重点看,哪些可以不看的,顺便让他给我讲解了一下线程池,我要记录下来,这样才能成长!
首先,我之前的一个理解,有线程就一定有任务,线程和任务是分不开的,这样导致我在看到线程池这个概念的时候就很晕。现在看来,线程和任务其实是独立开的。
先看一下java.util.concurrent中的ThreadPoolExecutor类,JDK API 1.6中说明:“一个
ExecutorService
,它使用可能的几个池线程之一执行每个提交的任务,通常使用
Executors
工厂方法配置。
线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行任务集时使用的线程)的方法。每个
ThreadPoolExecutor 还维护着一些基本的统计数据,如完成的任务数。 ”
稍微有点抽象,线程池可以看做一个Queue,里面存着一些可以使用的线程,这些线程是一直存在的,因为每次任务来的时候就分配一个线程给它去执行,这样就节省了线程的new和销毁的动作,会提升性能(这个应该是上面讲的线程池解决的第一个问题)。那么这些可使用的线程有多少呢?看一下这个构造函数:
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;
}
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;
}
“ThreadPoolExecutor 将根据 corePoolSize和 maximumPoolSize(参见
设置的边界自动调整池大小。当新任务在方法
execute(java.lang.Runnable)
中提交时,如果运行的线程少于 corePoolSize,则创建新线程来处理请求,即使其他辅助线程是空闲的。如果运行的线程多于 corePoolSize 而少于
maximumPoolSize,则仅当队列满时才创建新线程。(最开始没有理解,因为又把线程和任务混淆了,以为运行的线程数就是任务数,汗,队列满这三个字才与任务有关的了,也就是运行的任务数与线程数相同,但是又有新任务来的时候,这个时候就要new一个线程了)如果设置的 corePoolSize 和 maximumPoolSize
相同,则创建了固定大小的线程池。如果将 maximumPoolSize 设置为基本的***值(如
Integer.MAX_VALUE),则允许池适应任意数量的并发任务。在大多数情况下,核心和最大池大小仅基于构造来设置,不过也可以使用 setCorePoolSize(int)
和 setMaximumPoolSize(int)
进行动态更改”如果池中当前有多于 corePoolSize 的线程,则这些多出的线程在空闲时间超过 keepAliveTime 时将会终止(core和普通的就是不一样啊)。这提供了当池处于非活动状态时减少资源消耗的方法。如果池后来变得更为活动,则可以创建新的线程。也可以使用方法
setKeepAliveTime(long,
java.util.concurrent.TimeUnit)
动态地更改此参数。使用 Long.MAX_VALUE TimeUnit.NANOSECONDS
的值在关闭前有效地从以前的终止状态禁用空闲线程。默认情况下,保持活动策略只在有多于 corePoolSizeThreads 的线程时应用。但是只要
keepAliveTime 值非 0,allowCoreThreadTimeOut(boolean)
方法也可将此超时策略应用于核心线程。 ”“排队(三种策略不写了)
所有
BlockingQueue
都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:- 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队
- 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。
- 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。 ”
另外线程池占用内存大小,可以从jvm的内存配置中每个线程栈大小的配置中估算:-Xss256k。
下面是这两天看的项目代码:
public class TaskDispatcher {
......
public void init() {
// 初始化线程池
criticalPool = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, this.keepAliveTime,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(this.queueSize));
commonPool = new ThreadPoolExecutor(0, this.maxPoolSize / 2, this.keepAliveTime, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(this.queueSize));
// 设置线程池可用的标志
criticalPoolValid = true;
if (log.isInfoEnabled()) {
log.info("TaskDispatcher initialized");
}
}
}
......
public void init() {
// 初始化线程池
criticalPool = new ThreadPoolExecutor(this.corePoolSize, this.maxPoolSize, this.keepAliveTime,
TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(this.queueSize));
commonPool = new ThreadPoolExecutor(0, this.maxPoolSize / 2, this.keepAliveTime, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(this.queueSize));
// 设置线程池可用的标志
criticalPoolValid = true;
if (log.isInfoEnabled()) {
log.info("TaskDispatcher initialized");
}
}
}
criticalPool.execute(new Runnable() {
public void run() {
handler.handle(task);
}
});
public void run() {
handler.handle(task);
}
});
commonPool.execute(new Runnable() {
public void run() {
handler.handle(task);
}
});
public void run() {
handler.handle(task);
}
});
ok,至此,终于看明白任务分配这部分代码了。加油,may!