Java核心技术面试精讲
杨晓峰
前Oracle首席工程师
立即订阅
43250 人已学习
课程目录
已完结 43 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 以面试题为切入点,有效提升你的Java内功
免费
模块一 Java基础 (14讲)
第1讲 | 谈谈你对Java平台的理解?
第2讲 | Exception和Error有什么区别?
第3讲 | 谈谈final、finally、 finalize有什么不同?
第4讲 | 强引用、软引用、弱引用、幻象引用有什么区别?
第5讲 | String、StringBuffer、StringBuilder有什么区别?
第6讲 | 动态代理是基于什么原理?
第7讲 | int和Integer有什么区别?
第8讲 | 对比Vector、ArrayList、LinkedList有何区别?
第9讲 | 对比Hashtable、HashMap、TreeMap有什么不同?
第10讲 | 如何保证集合是线程安全的? ConcurrentHashMap如何实现高效地线程安全?
第11讲 | Java提供了哪些IO方式? NIO如何实现多路复用?
第12讲 | Java有几种文件拷贝方式?哪一种最高效?
第13讲 | 谈谈接口和抽象类有什么区别?
第14讲 | 谈谈你知道的设计模式?
模块二 Java进阶 (16讲)
第15讲 | synchronized和ReentrantLock有什么区别呢?
第16讲 | synchronized底层如何实现?什么是锁的升级、降级?
第17讲 | 一个线程两次调用start()方法会出现什么情况?
第18讲 | 什么情况下Java程序会产生死锁?如何定位、修复?
第19讲 | Java并发包提供了哪些并发工具类?
第20讲 | 并发包中的ConcurrentLinkedQueue和LinkedBlockingQueue有什么区别?
第21讲 | Java并发类库提供的线程池有哪几种? 分别有什么特点?
第22讲 | AtomicInteger底层实现原理是什么?如何在自己的产品代码中应用CAS操作?
第23讲 | 请介绍类加载过程,什么是双亲委派模型?
第24讲 | 有哪些方法可以在运行时动态生成一个Java类?
第25讲 | 谈谈JVM内存区域的划分,哪些区域可能发生OutOfMemoryError?
第26讲 | 如何监控和诊断JVM堆内和堆外内存使用?
第27讲 | Java常见的垃圾收集器有哪些?
第28讲 | 谈谈你的GC调优思路?
第29讲 | Java内存模型中的happen-before是什么?
第30讲 | Java程序运行在Docker等容器环境有哪些新问题?
模块三 Java安全基础 (2讲)
第31讲 | 你了解Java应用开发中的注入攻击吗?
第32讲 | 如何写出安全的Java代码?
模块四 Java性能基础 (3讲)
第33讲 | 后台服务出现明显“变慢”,谈谈你的诊断思路?
第34讲 | 有人说“Lambda能让Java程序慢30倍”,你怎么看?
第35讲 | JVM优化Java代码时都做了什么?
模块5 Java应用开发扩展 (4讲)
第36讲 | 谈谈MySQL支持的事务隔离级别,以及悲观锁和乐观锁的原理和应用场景?
第37讲 | 谈谈Spring Bean的生命周期和作用域?
第38讲 | 对比Java标准NIO类库,你知道Netty是如何实现更高性能的吗?
第39讲 | 谈谈常用的分布式ID的设计方案?Snowflake是否受冬令时切换影响?
周末福利 (2讲)
周末福利 | 谈谈我对Java学习和面试的看法
周末福利 | 一份Java工程师必读书单
结束语 (1讲)
结束语 | 技术没有终点
Java核心技术面试精讲
登录|注册

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

杨晓峰 2018-06-23
我在专栏第 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/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《Java核心技术面试精讲》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(28)

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

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

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

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

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

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

    2018-06-23
    13
  • Harry陈祥
    老师您好。有次面试,面试官问:为什么java的线程池当核心线程满了以后,先往blockingQueue中存任务,queue满了以后才会创建非核心线程? 是在问,为什么要这么设计?
    请问这个问题应该怎么回答?
    2019-02-12
    3
    10
  • 沈琦斌
    老师,我想问的是cache的线程池大小是1,每次还要新创建,那和我自己创建而不用线程池有什么区别?

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

    2018-06-28
    6
  • nb Ack
    阻塞性:
    BlockQueue存入任务队列时是没有阻塞,使用的是offer,无阻塞添加方法。
    BlockQueue取出任务队列时是有阻塞,有超时使用poll取值,无超时使用take阻塞方法取值

    添加任务逻辑:
    1.当任务数小于核心线程数,新建核心线程来执行任务
    2.任务数大于核心线程数,队列不满,放入任务队列
    3.任务数大于核心线程数,队列已满,新建线程执行
    4.任务数大于核心线程数,队列已满,工作线程已达最大线程数,拒绝任务,抛出异常(而不是阻塞任务,等待进入队列)
    2019-05-22
    2
  • Ifdevil
    老师您好,我看了线程池源码,里面是用HashSet存放worker的,为什么这里用hashset呢?去重?线程池需要去重吗?
    2019-03-04
    2
  • 饭粒
    写了个简单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);
    }
    ```

    作者回复: 实践是好习惯

    2019-01-15
    2
  • 我的理解,设置非核心线程的目的是防止任务数的段时间激增,导致任务数过多,从而核心线程处理时间太长。正常情况下要保证线程数小于核心线程数,非核心线程会过一段时间就被移出,保证了资源的利用,而核心一般不会变少
    2019-03-03
    1
  • 杨老师,我照着文章翻看源码,下面那块是不是不太对?
    ----------------
    Executors 目前提供了 5 种不同的线程池创建配置:

    newSingleThreadExecutor,它创建的是个 FinalizableDelegatedExecutorService

    newSingleThreadScheduledExecutor 创建的是 ScheduledThreadPoolExecutor

    作者回复: 谢谢指出

    2018-07-04
    1
  • 镰仓
    听了一段时间课程,质量很高。我的需求是android JavaVM

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

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

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

    2018-06-25
    1
  • Paul Shan
    我个人更倾向于用rxjava解决多线程的问题而不是直接操作线程池。
    2019-11-26
  • Paul Shan
    newCachedThreadPool() 60s时间窗口缓存线程,适合的场景是系统需要的线程数在每分钟是差不多的。
    newFixedThreadPool(int nThreads),设定了活动线程的最大值,如果超过这个数目,线程进入等待状态。适合场景例如总共只有n个CPU,为了提高效率,最多的并行也就是n。
    newSingleThreadExecutor(),单线程执行,可以保证多个线程的执行顺序。
    newSingleThreadScheduledExecutor() 和 newScheduledThreadPool(int corePoolSize) 周期性调度
    newWorkStealingPool(int parallelism) 并行处理线程
    2019-11-26
  • SevenSir
    学习了一下,自己总结一下:https://www.jhonrain.org/2018/09/14/%E9%AB%98%E5%B9%B6%E5%8F%91-%E7%BA%BF%E7%A8%8B%E6%B1%A0%E5%89%96%E6%9E%90/
    2019-08-18
  • bruce
    杨老师,文章中你指出newCachedThreadPool使用于大量短时间任务的场景。大量短时间任务这个是如何定义的,是否可以给出一些具体的业务使用场景。谢谢!
    2019-08-12
  • 拯救地球好累
    为什么cache线程池用的是大小为0的队列呢?
    2019-05-21
  • 星辰
    core/max size都是1。但是后面的 我就不知道怎么验证了
    2019-04-28
  • 木刻
    老师好,如果我一台服务器上跑好几个程序,每个程序都有自己的线程池,那每个程序中的线程池数量都配置自己本程序根据CPU核数和IO等算出的理论值吗
    2019-03-19
  • 不告诉你
    无论是创建核心线程还是非核心线程,都需要获取全局锁。只有在工作队列满了以后才去创建非核心线程,应该就是为了在时间上尽量延后非核心线程的创建,为了线程池的性能做考虑吧。
    2019-02-22
收起评论
28
返回
顶部