关于线程池:Java并发编程面试必备之线程池

28次阅读

共计 3837 个字符,预计需要花费 10 分钟才能阅读完成。

什么是线程池

  • 是一种基于池化思维治理线程的工具。

池化技术:池化技术简略点来说,就是提前保留大量的资源,以备不时之需。比方咱们的对象池,数据库连接池等。

线程池益处

咱们为什么要应用线程池,间接 new thread start 不好吗?

  • 升高资源耗费: 通过反复利用已创立的线程来升高线程创立和销毁所造成的耗费。
  • 进步响应速度: 工作达到时,能够立刻执行,不须要等到线程创立再来执行工作。
  • 进步线程的可管理性: 线程是稀缺资源,如果无限度创立,不仅会耗费系统资源,还会因为线程的不合理散布导致资源调度失衡,升高零碎的稳定性。应用线程池能够进行对立的调配、调优和监控。

线程池的执行流程

咱们先来看看线程池的一个执行流程图,此图来自文末参考 1

通过上述图咱们能够得出线程池执行工作能够有以下几种状况:

  • 如果以后的运行线程小于coreSize,则创立新线程来执行工作。
  • 如果以后运行的线程等于 coreSize 或多余 coreSize(动静批改了coreSize 才会呈现这种状况),把工作放到阻塞队列中。
  • 如果队列已满无奈将新退出的工作放进去的话,则须要创立新的线程来执行工作。
  • 如果新创建线程曾经达到了最大线程数,工作将会被回绝。

怎么是用线程池

java jdkExecutors有提供创立不同线程池的办法 (个别不举荐这种做法) 阿里巴巴的开发手册也明确强制规定不让通过 Executors 来创立的,在一些公司的开发标准外面应该也会有这么一条吧。

  • newFixedThreadPool
  • newSingleThreadExecutor
  • newCachedThreadPool
  • newScheduledThreadPool
  • newWorkStealingPool (jdk1.8 新增的)

咱们能够应用 ThreadPoolExecutor 来创立线程池

  public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) 

咱们能够看出创立线程池有七个参数,而上述咱们通过 Executors 工具类来创立的线程池就一两个参数,其余参数它都帮咱们默认写死了,咱们只有真正了解了这几个参数能力更好的去是用线程池。上面咱们来看看这七个参数(线程池参数)。

corePoolSize

  • 外围线程数(线程池的根本大小)当咱们提交一个工作到线程池时就会创立一个线程来执行工作. 当咱们须要执行的工作数大于外围线程数了就不再创立,

如果咱们调用了 prestartAllCoreThreads() 办法线程池就会为咱们提前创立好所有的根本线程。

maximumPoolSize

  • 最大线程数: 线程池容许创立的最大线程数。如果队列曾经满了,且已创立的线程数小于最大线程数,则线程池就会创立新的线程来执行工作。这里有个小知识点,如果咱们的队列是用的无界队列,这个参数是不会起作用的,因为咱们的工作会始终往队列中加,队列永远不会满(内存容许的状况)。

keepAliveTime

  • 闲暇线程最大生存工夫。以后线程数大于外围线程数时,完结多余的闲暇线程期待新工作的最长工夫。

默认状况下,只有当线程池中的线程数大于 corePoolSize 时,keepAliveTime才会起作用,直到线程池中的线程数不大于 corePoolSize,即当线程池中的线程数大于corePoolSize 时,如果一个线程闲暇的工夫达到 keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。然而如果调用了allowCoreThreadTimeOut(boolean) 办法,在线程池中的线程数不大于 corePoolSize 时,keepAliveTime参数也会起作用,直到线程池中的线程数为 0;
比方以后线程池中最大线程数 (maximumPoolSize) 为 50,外围线程数(corePoolSize)为 10,以后正在跑工作的线程数为 30. 而后是不是空出了 20 个线程没活干,所以这 20 个线程就要被消毁,有点卸磨杀驴的感觉。如果剩下的 30 个线程干完活了也劳动了 keepAliveTime 这么久,而后这 30 个线程外面也要被销毁 20 个,就保留个外围线程。如果设置了 allowCoreThreadTimeOut 等于 true 外围线程也会被销毁。
就跟咱们做外包我的项目一样,甲方我的项目实现了就得去另外一个甲方,如果短时间内都没有甲方接收你的话,你就要被解雇了,只会留下几个外围人员保护下我的项目,如果甲方我的项目保护的话用本人的人的话,所有的外包人会都会被解雇。

unit

  • 线程存活工夫的的单位。可选的单位有 dayshours 等。

workQueue

工作队列。能够抉择以下这些队列

threadFactory

用户设置创立线程的工厂,咱们能够通过这个工厂来创立有业务意义的线程名字。咱们能够比照下自定义的线程工厂和默认的线程工厂创创立的名字。

默认产生线程的名字 自定义线程工厂产生名字
pool-5-thread-1 testPool-1-thread-1

阿里开发手册也有明确说到,须要指定有意义的线程名字。

RejectedExecutionHandler

  • 线程池回绝策略。当队列和线程池都满了阐明线程池曾经处于饱和状态。必须要采取肯定的策略来解决新提交的工作。jdk 默认提供了四种回绝策略:

其实咱们也能够自定义工作回绝策略(实现下 RejectedExecutionHandler 接口),比如说如果工作回绝了咱们能够记录下日志,或者重试等,依据本人的业务需要来实现。

  • dubbo 工作回绝策略
  @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        String msg = String.format("Thread pool is EXHAUSTED!" +
                "Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed:"
                + "%d)," +
                "Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!",
            threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(),
            e.getLargestPoolSize(),
            e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(),
            url.getProtocol(), url.getIp(), url.getPort());
        logger.warn(msg);
        dumpJStack();
        dispatchThreadPoolExhaustedEvent(msg);
        throw new RejectedExecutionException(msg);
    }

咱们能够看出 dubbo 的回绝策略次要记录了具体的级别为 warm 的日志、输入以后线程堆栈详情、持续抛出回绝工作异样。

线程池参数如何设置?

线程池既然有这么多参数那么咱们如何去依据本人的业务理论状况来去正当的设置每个参数?

  • 个别咱们如果工作为耗时 IO 型比方读取数据库、文件读写以及网略通信的的话这些工作不会占据很多 cpu 的资源然而会比拟耗时:线程数设置为 2 倍 CPU 数以上,充沛的来利用 CPU 资源。
  • 个别咱们如果工作为 CPU 密集型的话比方大量计算、解压、压缩等这些操作都会占据大量的 cpu。所以针对于这种状况的话个别设置线程数为:1 倍 cpu+1。为啥要加 1,很多说法是备份线程。
  • 如果既有 IO 密集型工作,又有 CPU 密集型工作,这种该怎么设置线程大小?这种的话最好离开用线程池解决,IO密集的用 IO 密集型线程池解决,CPU密集型的用 cpu 密集型解决。

以上都只是理算状况下的估算而已,真正的正当参数还是须要看看理论生产运行的成果来正当的调整的。

监控线程池

  • 线程池工作是否饱和?线程的状况如何?总共执行了多少个工作?当初线程池的运行状况如何?队列外面是否有沉积工作?面对下面这些问题,线程池也有提供一些办法能够让咱们来查看下面这些指标。


有了这些参数咱们是不是调整线程池的参数就更加不便了。或者依据线程池的沉闷水平咱们主动来调节(动静调整下篇再来说)线程池的参数。

对于线程池的几个问题

  • 线程池是否辨别外围线程和非核心线程?
  • 如何保障外围线程不被销毁?
  • 线程池的线程是如何做到复用的?

以上几个小问题咱们去看看线程池的源码,这几个问题应该就不成问题了,咱们下篇见。

完结

  • 因为本人满腹经纶,难免会有纰漏,如果你发现了谬误的中央,还望留言给我指出来, 我会对其加以修改。
  • 如果你感觉文章还不错,你的转发、分享、赞叹、点赞、留言就是对我最大的激励。
  • 感谢您的浏览, 非常欢送并感谢您的关注。

  • 伟人肩膀摘苹果

https://tech.meituan.com/2020…
《java 并发编程实战》

正文完
 0