自动创建线程池的风险

线程池手动创建还是自动创建

  • 手动创建更好,因为这样可以更加明确线程池的运行规则,避免资源耗尽的风险

自动创建线程池(即直接调用JDK封装好的构造方法)可能带来哪些问题?

  • newFixedThreadPool
    • 容易造成大量内存占用,可能会导致OOM

演示newFixedThreadPool:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 演示newFixedThreadPool
 */
public class FixedThreadPoolTest {

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(4);
        for (int i = 0; i < 1000; i++) {
            executorService.execute(new Task());
        }
    }

}

class Task implements Runnable {
    @Override
    public void run(){
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName());
    }
}

首先创建一个mian函数,用Executors这个辅助工具类去新建出来,Executors.newFixedThreadPool()里面传入一个参数(想让它存在的线程数),也就是说用了这种线程池的话,看它名字Fixed(固定)Thread(线程)Pool(池),那么固定的想让它为几都可以,这里设为4。

创建一个任务,休眠500毫秒打印出线程名字。用for循环执行任务,执行1000次。

输出结果:

可以看出结果它的名字始终是1234,不会超过。这是因为执行这1000个任务的线程呢,它们是被规定好的就是4,所以即便任务来了,来了特别多,也不会创建新的线程去执行它,这是为什么呢?

我们看看它的源码(Mac 按住 command + 鼠标点击 newFixedThreadPool):

我们可以看到,源码里面的参数:

  • 第1个参数nThreads:n就是我们传进来的,这个参数原本是corePoolSize,核心数量就被设定为n,假设刚才是4,它就是4。
  • 第2个参数nThreads:是最大的线程数量,可以看到它设置的是和核心数一样的,它永远不可能膨胀超过4。
  • 第3个keepAliveTime存活时间:设置为0,因为第2个参数不可能膨胀到超过线程数量,所以这个参数是没有意义的,也不会有线程被回收。
  • 第4个是时间的单位:是毫秒,和第3个参数是绑定的,用来表示超时时间的。
  • 第5个参数:是队列,放的是LinkedBlockingQueue(无界队列),所以有再多任务进来都可以放到LinkedBlockingQueue去执行。

newFixedThreadPool这种线程池,我们传进去的工作队列它没有容量上限,所以请求越来越多的时候会导致OOM错误。

演示OOM:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 演示newFixedThreadPool出错的情况
 */
public class FixedThreadPoolOOM {
    private static ExecutorService executorService = Executors.newFixedThreadPool(1);

    public static void main(String[] args) {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            executorService.execute(new SubThread());
        }
    }
}

class SubThread implements Runnable {

    @Override
    public void run() {
        try {
            Thread.sleep(1000000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

写一个线程池,固定数量的线程池,假设就1个线程,因为我们希望它处理越慢越好,队列里的东西越来越多来让它达到线程过多的效果。

我们要在里面放特别多的任务,多到整型的最大值,执行一个任务。

设计一个任务,只在里面睡觉,并且睡的特长,一直在睡觉。

为什么要这么设计,就是因为我们不想让这个任务执行完毕,执行完毕了,这个任务就结束了,进入到下一个任务。我们的目标是把这个队列给塞满,所以说我们希望,这里面的任务一个都不能结束,自然随着时间的增加。这个队列的里面的东西就越来越多,所以用for循环不停的塞任务。

同时,我们要稍微做一下调整,用默认的配置去做,要想达到溢出时间太长,所以我们把内存设置的稍微小一点,也不影响,只要能看到有内存溢出的发生,说明实际上这是很危险的:

开始执行程序(稍等一会,看多久能出现期待的情况):

随着一段时间的等待,已经出现了期待的情况:

报出了错误OutOfMemoryError,可以看到是在执行executorService.execute(new SubThread());这段代码报出来的,不能再往里面塞了,我们的内存占用的实在是太多了,所以这就演示了我们用这种 newFixedThreadPool 带来的一种可能出错的情况。

Java

守护线程

2020-8-11 17:16:05

Python3

Python之多线程(四)多线程中的锁实现

2019-11-12 3:03:57

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索