线程池知识

一.简介
简单来说,就是维护一个池子,池子里面放了很多的线程。然后有任务,某个线程就获取这个任务来执行,任务执行完之后线程是不会释放掉的,而是停留在线程池里继续等待下一个任务。这样的好处是减少手动频繁的创建和销毁线程,因为线程是较重的资源,频繁的创建和销毁对降低系统性能。同时还具有解耦作用;线程创建于执行完全分开,方便维护。在这里插入图片描述
在 JDK 1.5 之后推出了相关的 api,常见的创建线程池方式有以下几种:

Executors.newCachedThreadPool():无限线程池。
Executors.newFixedThreadPool(nThreads):创建固定大小的线程池。
Executors.newSingleThreadExecutor():创建单个线程的线程池
实际上还是利用 ThreadPoolExecutor 类实现的。

优雅的关闭线程池

有运行任务自然也有关闭任务,从上文提到的 5 个状态就能看出如何来关闭线程池。
其实无非就是两个方法:
shutdown()/shutdownNow()。
但他们有着重要的区别:
shutdown() 执行后停止接受新任务,会把队列的任务执行完毕。
shutdownNow() 也是停止接受新任务,但会中断所有的任务,将线程池状态变为 stop。
两个方法都会中断线程,用户可自行判断是否需要响应中断。
springboot创建线程池
@Configuration
public class TreadPoolConfig {

/**
 * 消费队列线程
 * @return
 */
@Bean(value = "consumerQueueThreadPool")
public ExecutorService buildConsumerQueueThreadPool(){
    ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
            .setNameFormat("consumer-queue-thread-%d").build();

    ExecutorService pool = new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
            new ArrayBlockingQueue(5),namedThreadFactory,new ThreadPoolExecutor.AbortPolicy());

    return pool ;
}

}
使用时:

@Resource(name = "consumerQueueThreadPool")
private ExecutorService consumerQueueThreadPool;


@Override
public void execute() {

    //消费队列
    for (int i = 0; i < 5; i++) {
        consumerQueueThreadPool.execute(new ConsumerQueueThread());
    }

}

其实也挺简单,就是创建了一个线程池的 bean,在使用时直接从 Spring 中取出即可

线程池隔离

线程池看似很美好,但也会带来一些问题。
如果我们很多业务都依赖于同一个线程池,当其中一个业务因为各种不可控的原因消耗了所有的线程,导致线程池全部占满。

这样其他的业务也就不能正常运转了,这对系统的打击是巨大的。

比如我们 Tomcat 接受请求的线程池,假设其中一些响应特别慢,线程资源得不到回收释放;线程池慢慢被占满,最坏的情况就是整个应用都不能提供服务。

所以我们需要将线程池进行隔离。

通常的做法是按照业务进行划分:

比如下单的任务用一个线程池,获取数据的任务用另一个线程池。这样即使其中一个出现问题把线程池耗尽,那也不会影响其他的任务运行。

hystrix 隔离

这样的需求 Hystrix 已经帮我们实现了。

Hystrix 是一款开源的容错插件,具有依赖隔离、系统容错降级等功能。
二.线程池构造原理?
在这里插入图片描述
JDK源码就可以看到里面的代码如下在这里插入图片描述
简单来说就是构造了一个ThreadPoolExecutor对象实例,可大致就认为是一个线程池,里面有一些参数,这些参数大致包含了:

  1. corePoolSize:线程池的基本大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。这里需要注意的是:在刚刚创建ThreadPoolExecutor的时候,线程并不会立即启动,而是要等到有任务提交时才会启动。
  2. maximumPoolSize:线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。这里值得一提的是largestPoolSize,该变量记录了线程池在整个生命周期中曾经出现的最大线程个数。为什么说是曾经呢?因为线程池创建之后,可以调用setMaximumPoolSize()改变运行的最大线程的数目。
  3. keepAliveTime:线程空闲时间,默认为0.当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
  4. workQueue:它决定了缓存任务的排队策略。对于不同的应用场景我们可能会采取不同的排队策略,这就需要不同类型的队列。这个队列需要一个实现了BlockingQueue接口的任务等待队列。在ThreadPoolExecutor线程池的API文档中,一共推荐了三种等待队列,它们SynchronousQueue 、 ArrayBlockingQueue和LinkedBlockingQueue (无界)

三.线程池的运行原理
刚开始的时候其实线程池里是空的,是没有线程的。如果你此时提交一个任务进去,希望由线程池里的一个线程来执行。线程池会先看一下,现在池子里的线程数量有没有有达到corePoolSize指定的数量。现在线程池里的线程数量是0,然后corePoolSize是10,那么肯定没达到了,所以直接会在线程池里创建一个线程出来然后执行这个任务。![在这里插入图片描述](https://img-blog.csdnimg.cn/20200531154737123.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3l1cWlkYWc=,size_16,color_FFFFFF,t_70在这里插入图片描述假如这个线程处理完一个任务了,线程是不会被销毁的,他会一直等待下一个提交过来的任务。会用阻塞的方式尝试从任务队列里获取任务,如果队列是空的,他就会阻塞卡在那儿不动,直到有人放一个任务到队列里,他才会获取到一个任务然后继续执行,循环往复。在这里插入图片描述
再有任务过来时,线程池一判断发现,若线程数量比corePoolSize(10个)要小,那么继续直接在池子里创建一个线程,然后处理这个任务,处理完了继续尝试从workQueue里阻塞式获取任务。一直重复上面的操作,直到线程池里有10个线程了,达到了corePoolSize指定的数量。此时再有任务过来,就不需要创建任何一个额外的线程了,任务会全部直接入队到workQueue里。在这里插入图片描述
这里用的是一个无界的LinkedBlockingQueue,但是是一个有界的队列话,如果任务数超过队列长度就会无法入队。然后就会尝试再次在线程池里创建线程直到创建线程直到线程池里的线程数量达到maximumPoolSize指定的数量为止,假设maximumPoolSize为20,就有。在这里插入图片描述
然而,万一队列还是满了,然后线程池的线程数量达到了maximumPoolSize指定的数量,此时额外创建线程也无法创建了,会怎么办?
结果是:会reject掉,不让你继续提交任务了,此时默认的就是抛出一个异常。
对于线程池数量已经超过corePoolSize的,有一个keepAliveTime,超过指定时间获取不到任务就会自动释放掉了,也就是说这个线程就销毁了。
四。无界队列引发的内存飙升
对于无界的LinkedBlockingQueue,几乎可以无限制的放入任务到队列里。所以只要线程池里的线程数量达到了corePoolSize指定的数量之后,接下来就维持这个固定数量的线程了。那么此时万一每个线程获取到一个任务之后,他处理的时间特别特别的长,长到了令人发指的地步。比如处理一个任务要几个小时,此时会如何?当然会出现workQueue里不断的积压越来越多得任务,不停的增加。这个过程中会导致机器的内存使用不停的飙升,最后也许极端情况下就导致JVM OOM了,系统就挂掉了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值