抛弃了简单的new Thread(),加入了JAVA中的线程池,一路走来满是荆棘。在使用CachedThreadPool与FixedThreadPool发现了一些问题,这里作简要记录。
关于无限长线程池CachedThreadPool
首先上一下Executors.newCachedThreadPool()的源码
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
关于ThreadPoolExecutor类参数说明,这里就不赘述了,网上很多。
从源码可以看出CachedThreadPool所使用的线程最大数量为Integer.MAX_VALUE,也就是说在执行过程中只要前面的任务没有完成,那么线程数量就不断增加,这样的话会产生大量线程使用过程可能存在栈溢出的问题。
定长线程池FixedThreadPool
下面是Executors.newFixedThreadPool(10);的源码
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
定长线程池限制了最大的线程数量,解决了不定长线程池CachedThreadPool的无限线程的问题。然而定长的线程池使用了无限长的链式阻塞队列,这个队列无限大,如果任务执行慢且内存占用较多时,JVM会产生内存溢出。
个人常用解决方式
为了避免JVM内存问题,本人通常使用以下线程池的方式:
public static ExecutorService getFixedThreadPool(int nThread) {
return new ThreadPoolExecutor(nThread, nThread,
0L, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(nThread),new RejectedExecutionHandler() {
@Override
public void rejectedExecution(Runnable run, ThreadPoolExecutor executor) {
if(!executor.isTerminated())
try {
executor.getQueue().put(run);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
这里限定线程池大小和阻塞队列的大小,避免线程池的无限增大。然而当
阻塞队列满时,主线程(一般是生产者线程)会阻塞,等待队列中有任务执行完毕后才会继续添加任务,这样可以避免无限增长的线程池和无限增长的阻塞队列。这里唯一的缺陷是主线程需要等待子线程执行才能继续,所以使用时应该视情况而定。
使用ThreadPoolExecutor可以实例化需要的线程池,以上线程池需要看实际的情况来使用!