java使用线程池创建线程,java线程池创建的四种
00-1010先说结论。OOM风险显示内存溢出原因分析。使用ThreadPoolExecutor来改善创建线程池中的其他问题。总结前言:
在Java语言中,并发编程依赖于线程池,创建线程池的方法有很多种。但是从大的分类来说,线程池的创建可以分为两类:用ThreadPoolExecutor手动创建线程池和用Executors自动创建线程池。那么,您希望通过哪种方式创建线程池呢?今天就来详细聊一聊。
Java语言中的00-1010,一定要使用 ThreadPoolExecutor 手动的方式来创建线程池,因为这种方式可以通过参数来控制最大任务数和拒绝策略,让线程池的执行更加透明和可控,并且可以规避资源耗尽的风险。
00-1010如果我们用Executors自动创建线程池的方式来创建线程池,会有线程溢出的风险。
以 CachedThreadPool 为例我们来演示一下:
导入Java . util . ArrayList;导入Java . util . list;导入Java . util . concurrent . executorservice;导入Java . util . concurrent . executors;public threadpoolexecutorexample { static class oom class {//创建一个1MB大小的变量(1m=1024kb=1024 * 1024 byte)private byte[]data _ byte=new byte[1 * 1024 * 1024];} public static void main(string[]args)抛出中断异常{//自动用executors创建线程池Executorservice thread pool=executors . new cached thread pool();ListObject list=new ArrayList();//为(int i=0)添加任务;i 10I){ int finalI=I;thread pool . execute(new runnable(){ @ override public void run(){//定期添加try { thread . sleep(finalI * 200);} catch(interrupted exception e){ e . printstacktrace();}//将1M对象添加到集合oom class=new oom class();list . add(oom class);System.out.println(执行任务: finalI );} });} } }第 2 步将 Idea 中 JVM 最大运行内存设置为 10M(设置此值主要是为了方便演示),如下图所示:
以上程序的执行结果如下图所示:
从以上结果可以看出,线程执行7次后,OutOfMemoryError内存溢出异常开始出现。
目录
想要了解内存溢出的原因,我们需要查看 CachedThreadPool 实现的细节,它的源码如下图所示:
构造函数的第二个参数设置为整数。MAX_VALUE,表示最大值。
线程数,所以由于 CachedThreadPool 并不限制线程的数量,当任务数量特别多的时候,就会创建非常多的线程。而上面的 OOM 示例,每个线程至少要消耗 1M 大小的内存,加上 JDK 系统类的加载也要占用一部分的内存,所以当总的运行内存大于 10M 的时候,就出现内存溢出的问题了。
使用ThreadPoolExecutor来改进
接下来我们使用 ThreadPoolExecutor 来改进一下 OOM 的问题,我们使用 ThreadPoolExecutor 手动创建线程池的方式,创建一个最大线程数为 2,最多可存储 2 个任务的线程池,并且设置线程池的拒绝策略为忽略新任务,这样就能保证线程池的运行内存大小不会超过 10M 了,
实现代码如下:
import java.util.ArrayList;import java.util.List;import java.util.concurrent.*;/** * ThreadPoolExecutor 演示示例 */public class ThreadPoolExecutorExample { static class OOMClass { // 创建 1MB 大小的变量(1M = 1024KB = 1024*1024Byte) private byte[] data_byte = new byte[1 * 1024 * 1024]; } public static void main(String[] args) throws InterruptedException { // 手动创建线程池,最大线程数 2,最多存储 2 个任务,其他任务会被忽略 ThreadPoolExecutor threadPool = new ThreadPoolExecutor(2, 2, 0L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(2), new ThreadPoolExecutor.DiscardPolicy()); // 拒绝策略:忽略任务 List<Object> list = new ArrayList<>(); // 添加任务 for (int i = 0; i < 10; i++) { int finalI = i; threadPool.execute(new Runnable() { @Override public void run() { // 定时添加 try { Thread.sleep(finalI * 200); } catch (InterruptedException e) { e.printStackTrace(); } // 将 1m 对象添加到集合 OOMClass oomClass = new OOMClass(); list.add(oomClass); System.out.println("执行任务:" + finalI); } }); } // 关闭线程池 threadPool.shutdown(); // 检测线程池的任务执行完 while (!threadPool.awaitTermination(3, TimeUnit.SECONDS)) { System.out.println("线程池中还有任务在处理"); } }}
以上程序的执行结果如下图所示:
从上述结果可以看出,线程池从开始执行到执行结束都没有出现 OOM 的异常,这就是手动创建线程池的优势。
其他创建线程池的问题
除了 CachedThreadPool 线程池之外,其他使用 Executors 自动创建线程池的方式,也存在着其他一些问题,
比如 FixedThreadPool 它的实现源码如下:
而默认情况下任务队列LinkedBlockingQueue
的存储容量是 Integer.MAX_VALUE,也是趋向于无限大
如下图所示:
这样就也会造成,因为线程池的任务过多而导致的内存溢出问题。其他几个使用 Executors 自动创建线程池的方式也存在此问题,这里就不一一演示了。
总结
线程池的创建方式总共分为两大类:手动使用 ThreadPoolExecutor 创建线程池和自动使用 Executors 执行器创建线程池的方式。其中使用 Executors 自动创建线程的方式,因为线程个数或者任务个数不可控,可能会导致内存溢出的风险,所以在创建线程池时,建议使用 ThreadPoolExecutor 的方式来创建。
到此这篇关于Java创建线程池为什么一定要用ThreadPoolExecutor的文章就介绍到这了,更多相关Java线程池创建内容请搜索盛行IT以前的文章或继续浏览下面的相关文章希望大家以后多多支持盛行IT!
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。