面试3.3_Java_线程池

本文深入探讨Java线程池的设计原理及使用技巧,包括不同类型的线程池、配置参数的最佳实践、运作流程以及如何选择合适的拒绝策略。同时,还介绍了线程池中各种队列的特点与应用场景。


参考链接

  1. 线程池 1
  2. 线程池 2
  3. 线程池_AddWorker()
  4. addWorker详解

并发工具类

  1. CountDownLatch(倒计时器)协调多个线程间的同步。常用来控制线程等待
  2. CyclicBarrier(循环栅栏)所有线程都到达屏障后,才能继续执行
  3. Semaphore(信号量)允许多个线程同时访问某个资源

线程池

为什么要使用线程池

  1. 降低资源消耗通过重复利用已创建的线程,降低线程创建和销毁造成的消耗。
  2. 提高响应速度当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 增加线程的可管理性线程是稀缺资源,使用线程池可以进行统一分配,调优和监控。

Executors类创建四种常见线程池

  1. newSingleThreadExecutor单线程的线程池
  2. newFixedThreadPool固定大小的线程池
  3. newCachedThreadPool可缓存的线程池
  4. newScheduledThreadPool大小无限的线程池,支持定时以及周期性执行任务的需求。

线程池参数怎么配置

参考资料:CPU 密集型 和 IO密集型 的区别,如何确定线程池大小?

  1. 计算密集型

    1. 线程数 = CPU数 + 1,通常能实现最优的利用率。
    2. "+1"的理由:一个经验值。

      计算密集型的线程恰好在某时因为发生一个页错误或者因其他原因而暂停,刚好有一个“额外”的线程,可以确保在这种情况下CPU周期不会中断工作。

  2. I/O密集型

    1. 线程数 = CPU数 * 2 ,不是最优
    2. 线程数 = CPU数 * CPU利用率 * (任务等待时间 / 任务计算时间 + 1)
      1. CPU数量是确定的
      2. CPU使用率是目标值也是确定的
      3. W/C可通过基准程序测试得出的。
    3. 如果要求比较精确,可以通过压测来获取一个合理的值。

ThreadPoolExcutor参数

  1. corePoolSize(核心线程数):当线程池运行的线程少于 corePoolSize 时,将创建一个新线程来处理请求,即使其他工作线程处于空闲状态。
  2. maximumPoolSize(最大线程数):线程池允许开启的最大线程数。
  3. workQueue(队列):用于保留任务并移交给工作线程的阻塞队列。
  4. threadFactory(线程工厂):线程创建工厂。
  5. keepAliveTime(保持存活时间):如果线程池当前线程数超过 corePoolSize,则多余的线程空闲时间超过 keepAliveTime 时会被终止。
  6. timeunitkeepAliveTime的单位。
  7. handler(拒绝策略):触发拒绝策略的情况
    1)线程池运行状态不是 RUNNING
    2)线程池已经达到最大线程数,并且阻塞队列已满时。

ThreadPoolExcutor运作流程

在这里插入图片描述


AddWorker方法

  1. 自旋CAS操作来将线程数加1
  2. **新建一个线程加入至workers队列中,并启用 **
  3. Worker类继承自AQS,run方法中调用ThreadPoolExecutor的runworker方法
  4. runWorker函数中最重要的是getTask(),不断从阻塞队列中取任务交给线程执行

拒绝策略

  1. AbortPolicy中止策略抛出异常 RejectedExecutionException。调用者可以捕获这个异常,然后根据需求编写自己的处理代码。
  2. DiscardPolicy抛弃策略什么都不做,直接抛弃被拒绝的任务。
  3. DiscardOldestPolicy抛弃最老策略抛弃阻塞队列中最老的任务,相当于就是队列中下一个将要被执行的任务,然后重新提交被拒绝的任务。如果阻塞队列是一个优先队列,那么“抛弃最旧的”策略将导致抛弃优先级最高的任务,因此最好不要将该策略和优先级队列放在一起使用。
  4. CallerRunsPolicy调用者运行策略在调用者线程中执行该任务。该策略实现了一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将任务回退到调用者(调用线程池执行任务的主线程),由于执行任务需要一定时间,因此主线程至少在一段时间内不能提交任务,从而使得线程池有时间来处理完正在执行的任务。

线程池有哪些队列

  1. ArrayBlockingQueue:基于数组结构的有界阻塞队列

  2. LinkedBlockingQueue:基于链表结构的有界/无界阻塞队列,吞吐量通常高于ArrayBlockingQueue。

  3. DelayQueue:延迟队列,是一个任务定时周期的延迟执行的队列。

  4. PriorityBlockingQueue:具有优先级的无界队列

  5. SynchronousQueue:同步队列,不是真正的队列,只是线程之间移交的机制。Executors.newCachedThreadPool使用了该队列。

使用队列需要注意的地方

  1. 使用有界队列时,需要注意线程池满了后,被拒绝的任务如何处理。

  2. 使用无界队列时,需要注意如果任务的提交速度大于线程池的处理速度,可能会导致内存溢出


核心线程怎么实现一直存活

在获取任务时,通过阻塞队列的 take() 方法实现的 一直阻塞(存活)。

非核心线程如何实现在 keepAliveTime 后死亡?

利用阻塞队列的方法,在获取任务时通过阻塞队列的 poll(time,unit) 方法实现的 延迟死亡


评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值