java-学习篇-线程池

线程越多越好?

时间资源

  • 线程创建、销毁需要时间。
  • 操作系统需要频繁切换上下文,过多线程的调度影响性能。

空间资源

  • java 对象占用堆内存,操作系统线程占用系统内存。
  • JVM 规定一个线程默认最大栈大小1M,栈空间需要从系统内存中分配。

线程池好处

  • 降低资源消耗:通过池化技术重复利用已创建的线程,降低线程创建和销毁造成的损耗。
  • 提高响应速度:任务到达时,无需等待线程创建即可立即执行。
  • 提高线程的可管理性
  • 提供更多更强大的功能

线程池运行机制

线程池在内部实际上构建了一个生产者消费者模型,将线程和任务两者解耦,并不直接关联,从而良好的缓冲任务,复用线程。

线程池的运行主要分成两部分:任务管理、线程管理。
任务管理部分充当生产者的角色,当任务提交后,线程池会判断该任务后续的流转:(1)直接申请线程执行该任务;(2)缓冲到队列中等待线程执行;(3)拒绝该任务。
线程管理部分是消费者,它们被统一维护在线程池内,根据任务请求进行线程的分配,当线程执行完任务后则会继续获取新的任务去执行,最终当线程获取不到任务的时候,线程就会被回收。

ThreadPoolExecutor

package java.util.concurrent;
public class ThreadPoolExecutor extends AbstractExecutorService
public abstract class AbstractExecutorService implements ExecutorService

// 扩充执行任务的能力;提供了管控线程池的方法
public interface ExecutorService extends Executor 

// 完成线程的调配和任务的执行部分
public interface Executor
void execute(Runnable command);

从以下三部分详细讲解线程池运行机制:

  1. 线程池如何维护自身状态
  2. 线程池如何管理任务
  3. 线程池如何管理线程
// 线程池的运行状态和线程池中有效线程的数量进行控制的一个字段。包含两部分信息:高3位保存runState,低29位保存workerCount
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

ThreadPoolExecutor 的运行状态有5种,分别为:

private static final int COUNT_BITS = Integer.SIZE - 3;

// runState is stored in the high-order bits
// 能接受新提交的任务,并且也能处理阻塞队列中的任务
private static final int RUNNING    = -1 << COUNT_BITS;
// 关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务
private static final int SHUTDOWN   =  0 << COUNT_BITS;
// 不能接受新任务,也不处理队列中的任务,会中断正在处理任务的线程
private static final int STOP       =  1 << COUNT_BITS;
// 所有的任务都已终止了,workerCount(有效线程数)为0
private static final int TIDYING    =  2 << COUNT_BITS;
// 在 terminated() 方法执行完后进入该状态
private static final int TERMINATED =  3 << COUNT_BITS;

任务调度

  1. 线程池是否 RUNNING 状态
  2. 如果 workerCount < corePoolSize,则创建并启动一个线程来执行新提交的任务
  3. 如果 workerCount >= corePoolSize,且线程池内的阻塞队列未满,则将任务添加到该阻塞队列中
  4. 如果 workerCount >= corePoolSize && workerCount < maximumPoolSize,且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务
  5. 如果 workerCount >= maximumPoolSize,且线程池内的阻塞队列已满,则根据拒绝策略来处理该任务,默认的处理方式是直接抛异常

任务缓冲
任务缓冲模块是线程池能够管理任务的核心部分。线程池的本质是对任务和线程的管理,而做到这一点最关键的思想就是将任务和线程两者解耦,不让两者直接关联,才可以做后续的分配工作。线程池是以生产者消费者模式,通过一个阻塞队列来实现的。

任务拒绝

public interface RejectedExecutionHandler {
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
// Worker 是通过继承 AQS,使用 AQS 来实现独占锁这个功能。
private final class Worker
    extends AbstractQueuedSynchronizer
    implements Runnable

参考资料

  • JDK 1.8 源码

猜你喜欢

转载自blog.csdn.net/u010019244/article/details/105333828
今日推荐