Java并发工具之线程池

线程池通过减少线程的创建和销毁开销,提高响应速度,合理利用资源。本文详细介绍了线程池的创建、配置、停止方法,以及线程池的五种状态,线程数量设定原则,任务拒绝策略,常用线程池特点和使用注意事项。
摘要由CSDN通过智能技术生成

1. 什么是线程池?

  1. 线程的创建和销毁是有一定的开销的,为了减少开销(内存和垃圾回收),我们通过创建线程池来执行大量的任务,避免反复创建并销毁线程所带来的问题,如果不用线程池,一旦任务数量过多,新建线程过多可能会OOM异常,即内存溢出
  2. 好处:
    • 限制线程资源的总量,复用每一个线程,同一管理资源
    • 加快响应速度,合理利用CPU和内存
  3. 适用场景:
    1. 服务器接收大量请求(Tomcat本身就是用线程池来实现的)
    2. 开发中,如果需要创建5个以上的线程,就可以用线程池

2. 创建和停止线程池?

2.1. 线程池构造函数的参数

参数 含义
corePoolSize 核心线程数
maxPoolSize 最大线程数
keepAliveTime 保持存活时间
workQueue 任务存储队列
threadFactory 当线程池需要新的线程的时候,会使用threadFactory来生成新的线程
Handler 由于线程池无法接受你所提交的任务的拒绝策略
  1. corePoolSize :线程完成初始化后,默认情况下,线程池中并没有任何线程,线程池会等待有任务到来时,再创建新线程去执行任务;

  2. maxPoolSize:线程池有可能会在核心线程数的基础上,额外增加一些线程,但是这些新增加的线程数有一个上线,就是最大线程数;

    什么情况下扩展到maxPoolSize?

      1. 如果线程数小于corePoolSize ,即使其他工作线程处于空闲状态,也会创建一个新线程来运行新任务;
      2. 如果线程数等于或大于corePoolSize 但是少于maxPoolSize,则将任务放入workQueue队列(指定队列的容量);
      3. 如果队列已满,并且线程数小于maxPoolSize,则创建一个新线程来运行任务;
      4. 如果队列已满,并在线程数大于maxPoolSize,则执行Handler拒绝策略;
    

    总之,是否需要增加线程的判断顺序是:

     corePoolSize  -》workQueue-》maxPoolSize
    

    增减线程的特点:

      1. 通过corePoolSize 和maxPoolSize相同,就可以创建固定大小的线程池;
      2. 线程池希望保持较少的线程数,并且只有在负载变得很大的时候才增加它;
      3. 通过设置maxPoolSize为很高的值,可以允许线程池容纳任意数量的并发任务;
      4. 只有在队列填满时才多于corePoolSize 的线程,所以如果你使用的是无界队列,那么线程数就不会超过corePoolSize ;
    
  3. keepAliveTime:如果线程池当前的线程数多于corePoolSize ,那么如果多余的线程空闲时间超过keepAliveTime,它们就会被终止,比如:corePoolSize 为5,当前线程数为10,如果线程空闲了,就会恢复到5;

  4. ThreadFactory:用来创建线程,默认是非守护线程,优先级是5,一共有10个等级;

  5. workQueue:有3种最常见的队列类型:

     1. 直接交接:SynchronousQueue,本身没有容量,maxpoolSize要设置大一些;
     2. 无界队列:LinkedBlockingQueue,maxpoolSize设置多大都没有用,会一直缓存新增的任务,有可能造成OOM;
     3. 有界队列:ArrayBlockingQueue,队列容量指定,满了之后,就创建新的线程;
    

2.2 线程池应该手动创建还是自动创建

Excutors类创建线程池的方法如下:

1. newFixedThreadPool:由于传进去的LinkedBlockingQueue是没有容量上限的,所以当请求数越来越多,并且无法及时处理完毕的时候,也就是请求堆积的时候,会容易造成OOM。
2. newSingleThreadExecutor:和newFixedThreadPool的原理基本一样,都采用LinkedBlockingQueue无界队列,只不过把corePoolSize 和maxPoolSize都改成了1,请求堆积的时候也会占用大量的内存。
3. newCachedThreadPool:可缓存线程池,使用SynchronousQueue直接队列,corePoolSize 为0,maxPoolSize设置为Integer.Max_Value,线程可以无限创建,但是请求过多且无法及时处理完毕,也会导致OOM。
4. newScheduledThreadPool:支持定时以及周期性任务执行的线程池,采用DelayedWorkQueue延迟队列,可以指定延迟时间执行任务,还可以以一定时间间隔来运行。

正确创建线程池的方法是手动创建:根据不同的业务场景,自己设置线程池参数,比如内存大小,线程名字等等。

2.3 线程池里的线程数量设定为多少比较合适?

  • CPU密集型:比如八核处理器,那可以把corePoolSize 设置为核心数的1-2倍;
  • 耗时IO型(读写文件):这种类型的CPU是不工作的,可以设置为核心数的很多倍
  • 线程数=CPU核心数*(1+平均等待时间/平均工作时间)

2.4 停止线程池的正确方法

  • shutdown,初始化整个关闭过程,不是直接关闭,把正在执行以及队列中等待的任务执行完毕再关闭,然后拒绝新的任务;
public class StopThreadPool {
   
    public static void main(String[] args) throws InterruptedException {
   
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i = 0; i < 50; i++) {
   
            executorService.execute(new Runnable() {
   
                @Override
                public void run() {
   
                    try {
   
                        Thread.sleep(500);
                        System.out.println("执行已经提交的线程:" +Thread.currentThread().getName());
                    } catch (InterruptedException e) {
   
                        e.printStackTrace();
                    }
                }
            });
        }
        Thread.sleep(1000);
        executorService.shutdown();
        executorService.execute(new Runnable() {
   
            @Override
            public void run() {
   
                System.out.println("继续提交线程池");
            }
        });
    }
}

执行已经提交的线程:pool-1-thread
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值