【线程池】 正确创建线程池的方法

正确创建线程池的方法

线程池可以通过Executors工具类自动创建,也可以手动创建。对于如何选择,《Java开发手册》有如下描述:

线程池不允许使用Executors创建,而是通过ThreadPoolExecutor方式。这样可以使开发人员更加明确线程池的运行规则,规避资源耗尽的风险。
Executros生成线程池对象的弊端:

  • FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能堆积大量请求,导致OOM。
  • CachedThreadPool允许创建的线程数量为Integer.MAX_VALUE,可能创建大量的线程,导致OOM。
    在这里插入图片描述

FixedThreadPool内存溢出的示例

FixedThreadPool采用无界队列,容量上限是Integer.MAX_VALUE,当请求越来越多,并且无法及时处理完毕时,就会占用大量内存,导致OOM。

public class FixedThreadPoolOOM {
    
    
    public static void main(String[] args) {
    
    
        ExecutorService pool = Executors.newFixedThreadPool(1);
        for(int i = 0; i < Integer.MAX_VALUE; i++) {
    
    
            System.out.println(i);
            pool.execute(new Task());
        }
    }
}

class Task implements Runnable{
    
    

    @Override
    public void run() {
    
    
        try {
    
    
            TimeUnit.SECONDS.sleep(1000);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        }
    }
}

为了能够尽快内存溢出,调整JVM参数如下

-Xms8M -Xmx8M

运行结果:内存溢出时,工作队列中有164963个任务。

164963
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
	at java.lang.Integer.toString(Integer.java:403)
	at java.lang.String.valueOf(String.java:3099)
	at java.io.PrintStream.print(PrintStream.java:597)
	at java.io.PrintStream.println(PrintStream.java:736)
	at com.lzp.java.concurrent.threadpool.FixedThreadPoolOOM.main(FixedThreadPoolOOM.java:19)

核心线程数的计算

根据不同的业务场景,自己设置线程池参数,例如内存多大,线程名字等。

  • CPU密集型任务(加密、计算hash等):最佳线程数为CPU核心数的1-2倍。
  • 耗时IO型(读写数据库、文件、网络读写等):最佳线程数一般大于CPU核心数很多倍,以JVM线程监控显示繁忙情况为依据,保证线程空闲可以衔接上。估算公式为
线程数 = CPU核心数 * (1 + 平均等待时间 / 平均工作时间)

如果需要更加精准的数值,则需要进行压测。

猜你喜欢

转载自blog.csdn.net/LIZHONGPING00/article/details/105155932