Java 核心技术面试精讲
杨晓峰
前 Oracle 首席工程师
124523 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 44 讲
Java 核心技术面试精讲
15
15
1.0x
00:00/00:00
登录|注册

第21讲 | Java并发类库提供的线程池有哪几种? 分别有什么特点?

我在专栏第 17 讲中介绍过线程是不能够重复启动的,创建或销毁线程存在一定的开销,所以利用线程池技术来提高系统资源利用效率,并简化线程管理,已经是非常成熟的选择。
今天我要问你的问题是,Java 并发类库提供的线程池有哪几种? 分别有什么特点?

典型回答

通常开发者都是利用 Executors 提供的通用线程池创建方法,去创建不同配置的线程池,主要区别在于不同的 ExecutorService 类型或者不同的初始参数。
Executors 目前提供了 5 种不同的线程池创建配置:
newCachedThreadPool(),它是一种用来处理大量短时间工作任务的线程池,具有几个鲜明特点:它会试图缓存线程并重用,当无缓存线程可用时,就会创建新的工作线程;如果线程闲置的时间超过 60 秒,则被终止并移出缓存;长时间闲置时,这种线程池,不会消耗什么资源。其内部使用 SynchronousQueue 作为工作队列。
newFixedThreadPool(int nThreads),重用指定数目(nThreads)的线程,其背后使用的是无界的工作队列,任何时候最多有 nThreads 个工作线程是活动的。这意味着,如果任务数量超过了活动队列数目,将在工作队列中等待空闲线程出现;如果有工作线程退出,将会有新的工作线程被创建,以补足指定的数目 nThreads。
newSingleThreadExecutor(),它的特点在于工作线程数目被限制为 1,操作一个无界的工作队列,所以它保证了所有任务的都是被顺序执行,最多会有一个任务处于活动状态,并且不允许使用者改动线程池实例,因此可以避免其改变线程数目。
newSingleThreadScheduledExecutor() 和 newScheduledThreadPool(int corePoolSize),创建的是个 ScheduledExecutorService,可以进行定时或周期性的工作调度,区别在于单一工作线程还是多个工作线程。
newWorkStealingPool(int parallelism),这是一个经常被人忽略的线程池,Java 8 才加入这个创建方法,其内部会构建ForkJoinPool,利用Work-Stealing算法,并行地处理任务,不保证处理顺序。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结
仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《Java 核心技术面试精讲》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(37)

  • 最新
  • 精选
  • I am a psycho
    通过看源码可以得知,core和max都是1,而且通过FinalizableDelegatedExecutorService进行了包装,保证线程池无法修改。同时shutdown方法通过调用interruptIdleWorkers方法,去停掉没有工作的线程,而shutdownNow方法是直接粗暴的停掉所有线程。无论是shutdown还是shutdownNow都不会进行等待,都会直接将线程池状态设置成shutdown或者stop,如果需要等待,需要调用awaitTernination方法。查找了一下threadFactory的使用,只找到了在worker创建的时候,用来初始化了线程。

    作者回复: 不错,很棒的总结; 我问threadFactory次数,其实是问worker都在什么情况下会被创建,比如,比较特别的,任务抛异常时;随便自定义一个threadfactory,模拟提交任务就能体会到

    3
    63
  • 李二木
    我觉得还有一点很重要,就是放在线程池中的线程要捕获异常,如果直接抛出异常,每次都会创建线程,也就等于线程池没有发挥作用,如果大并发下一直创建线程可能会导致JVM挂掉。最近遇到的一个坑

    作者回复: 任务出异常是要避免

    5
    50
  • 约书亚
    疑问,为什么当初sun的线程池模式要设计成队列满了才能创建非核心线程?类比其他类似池的功能实现,很多都是设置最小数最大数,达到最大数才向等待队列里加入,比如有的连接池实现。

    作者回复: Doug Lea这个实现基本是工业标准了,除非特定场景需求

    10
    26
  • 沈琦斌
    老师,我想问的是cache的线程池大小是1,每次还要新创建,那和我自己创建而不用线程池有什么区别?

    作者回复: 你是说cachedthreadpool?那个大小是浮动的,不是1;如果说single,executorservice毕竟还提供了工作队列,生命周期管理,工作线程维护等很多事,还是要高效

    15
  • 饭粒
    写了个简单demo玩了下。 创建线程池会初始化线程工厂,工作线程是在提交任务的创建的。工作线程在执行任务中抛出异常,再次提交任务会又新建工作线程。newFixedThreadPool 正常执行任务时会优先创建线程已达到核心线程数,不会优先复用空闲工作线程。 ``` /** * 线程池工作线程执行任务抛出异常 */ @Test public void test03() throws InterruptedException { // java.util.concurrent.Executors.DefaultThreadFactory.DefaultThreadFactory 构造线程工厂 ExecutorService executorService = Executors.newCachedThreadPool(); Runnable task = new Runnable() { @Override public void run() { System.out.println("hello world"); // 抛出异常 throw new RuntimeException(); } }; executorService.execute(task); // 提交任务通过 DefaultThreadFactory.newThread() 创建线程 TimeUnit.SECONDS.sleep(2); // 前一个工作线程在执行任务中抛出异常,再提交任务又会新建工作线程 executorService.execute(task); TimeUnit.SECONDS.sleep(3); } ```

    作者回复: 实践是好习惯

    2
    11
  • 王磊
    core和max应该都是1。验证的方法是自己写一个Threadlocal, 里面有相应创建线程的日志,然后把它传入创建线程池。

    作者回复: core和Max源码或者逻辑分析都很清楚;而创建线程次数理论上是不确定的,比如任务执行中抛异常,就要重新创建worker

    6
  • GK java
    线程池到底需不需要关闭

    作者回复: 通常建议明确关闭,要看具体场景,我们的应用对于关闭本身是如何定义,有没有要求,什么时机触发,需要保证优雅的退出吗?

    5
  • 杨老师,我照着文章翻看源码,下面那块是不是不太对? ---------------- Executors 目前提供了 5 种不同的线程池创建配置: newSingleThreadExecutor,它创建的是个 FinalizableDelegatedExecutorService newSingleThreadScheduledExecutor 创建的是 ScheduledThreadPoolExecutor

    作者回复: 谢谢指出

    2
    5
  • 镰仓
    听了一段时间课程,质量很高。我的需求是android JavaVM

    作者回复: android我并没有特别的经验,尽管很多方面是通用的

    3
  • 灰飞灰猪不会灰飞.烟灭
    老师 放入队列中的线程是直接调用start方法还是把队列中的线程放入线程工厂,让线程工厂执行? 另外,怎么判断一个线程是否执行完成呢?(只有执行完成才返回结果)谢谢老师

    作者回复: 工厂是创建线程;执行完成通常是说任务,而不是线程,任务才是我们关心的;可以用Future

    2
收起评论
显示
设置
留言
37
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部