前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >面试必考——线程池原理概述

面试必考——线程池原理概述

作者头像
黑洞代码
发布2021-01-14 15:17:04
7170
发布2021-01-14 15:17:04
举报

线程池的源码解析较为繁琐。各位同学必须先大体上理解线程池的核心原理后,方可进入线程池的源码分析过程。

corePoolSize核心线程

线程池的基本大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。

核心线程是否会被回收?

  1. 核心线程将一直保存在线程池中
  2. 哪怕核心线程是处于空闲状态,也可以不回收
  3. allowCoreThreadTimeOut参数可以控制是否回收核心线程
  4. 在刚刚创建线程池的时候,核心线程并不会立即启动,而是要等到有任务提交时才会启动
  5. prestartCoreThread/prestartAllCoreThreads方法可以事先启动核心线程
  6. 线程池没有任务执行时,线程池内的线程数到底是多少?你得仔细想想怎么回答这个问题。

阻塞队列

当所有的核心线程处于工作状态时,再想向线程池提交任务,将无核心线程可以直接处理。阻塞队列就是保存这些将要被线程池执行的任务的。

阻塞队列注意事项?

  1. BlockingQueue接口的所有子类都可以作为阻塞队列
  2. 阻塞队列的容量问题值得注意。如果选择无界阻塞队列,即线程池的核心线程处于工作状态后,线程池将无限制的接收所有的任务,可能会造成阻塞队列过长,影响JVM性能,严重的情况下可能会造成OOM。
  3. 这也是为什么Alibaba Java Coding Guidelines开发插件不推荐直接使用Executors类的相关API直接创建线程池的原因。

maximumPoolSize最大线程

线程池中允许的最大线程数,线程池中的所有线程数目不会超过该值。如果线程池队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。这部分创建出的线程叫做非核心线程。

这里值得一提的是largestPoolSize,该变量记录了线程池在整个生命周期中曾经出现的最大线程个数。为什么说是曾经呢?因为线程池创建之后,可以调用setMaximumPoolSize()改变运行的最大线程的数目。

非核心线程是否会被回收?

  1. 非核心线程是会被回收的
  2. keepAliveTime/unit控制非核心线程最大的空闲时间,超过这个时间还没有任务可以执行,非核心线程将会被回收

线程工厂

顾名思义,现场工厂就是线程池创建线程的工厂。仅此而已???你觉得你去面试,以上回答能唬得住面试官吗?

尝试以下的面试技巧

  1. 我擅长设计模式
  2. 典型的设计模式——工厂设计模式
  3. 工厂模式的使用场景——在线程池中
  4. 面试官:“你了解过线程池的原理吗?可以介绍一下吗?”
  5. 接下来就是你忽悠面试官的过程了。因为你已经把线程池可能的考点和答案准备好了。

ThreadPoolExecutor使用了Executors提供的默认线程工厂如下。当然你要是看这个默认的线程工厂不爽,你也可以自定义线程工厂。

代码语言:javascript
复制
/**
 * The default thread factory
 */
static class DefaultThreadFactory implements ThreadFactory {
	private static final AtomicInteger poolNumber = new AtomicInteger(1);
	private final ThreadGroup group;
	private final AtomicInteger threadNumber = new AtomicInteger(1);
	private final String namePrefix;

	DefaultThreadFactory() {
		SecurityManager s = System.getSecurityManager();
		group = (s != null) ? s.getThreadGroup() :
							  Thread.currentThread().getThreadGroup();
		namePrefix = "pool-" +
					  poolNumber.getAndIncrement() +
					 "-thread-";
	}

	public Thread newThread(Runnable r) {
		Thread t = new Thread(group, r,
							  namePrefix + threadNumber.getAndIncrement(),
							  0);
		if (t.isDaemon())
			t.setDaemon(false);
		if (t.getPriority() != Thread.NORM_PRIORITY)
			t.setPriority(Thread.NORM_PRIORITY);
		return t;
	}
}

拒绝策略/饱和策略

当线程池的线程数达到最大,并且阻塞队列的容量也达到了最大的时候,线程池将达到最大的负载,此时如果再向线程池中添加任务,线程池将无法继续处理。线程池的拒绝策略就是为了保护线程池的负载。

ThreadPoolExecutor提供了4种拒绝策略。

如果你觉得不过瘾,你也可以实现RejectedExecutionHandler接口,构建你自己的线程池拒绝策略。

  1. CallerRunsPolicy

由线程池的调用线程(调用execute方法的线程)执行任务,线程池本身处于饱和状态时不再受理任何外部提交的任务。

代码语言:javascript
复制
/**
 * A handler for rejected tasks that runs the rejected task
 * directly in the calling thread of the {@code execute} method,
 * unless the executor has been shut down, in which case the task
 * is discarded.
 */
public static class CallerRunsPolicy implements RejectedExecutionHandler {
	/**
	 * Creates a {@code CallerRunsPolicy}.
	 */
	public CallerRunsPolicy() { }

	/**
	 * Executes task r in the caller's thread, unless the executor
	 * has been shut down, in which case the task is discarded.
	 *
	 * @param r the runnable task requested to be executed
	 * @param e the executor attempting to execute this task
	 */
	public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
		if (!e.isShutdown()) {
			r.run();
		}
	}
}
  1. AbortPolicy

通过抛出RejectedExecutionException异常的方式拒绝执行任务。

代码语言:javascript
复制
/**
 * A handler for rejected tasks that throws a
 * {@code RejectedExecutionException}.
 */
public static class AbortPolicy implements RejectedExecutionHandler {
	/**
	 * Creates an {@code AbortPolicy}.
	 */
	public AbortPolicy() { }

	/**
	 * Always throws RejectedExecutionException.
	 *
	 * @param r the runnable task requested to be executed
	 * @param e the executor attempting to execute this task
	 * @throws RejectedExecutionException always
	 */
	public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
		throw new RejectedExecutionException("Task " + r.toString() +
											 " rejected from " +
											 e.toString());
	}
}
  1. DiscardPolicy

悄悄地把任务丢弃,不做任何处理和记录

代码语言:javascript
复制
/**
 * A handler for rejected tasks that silently discards the
 * rejected task.
 */
public static class DiscardPolicy implements RejectedExecutionHandler {
	/**
	 * Creates a {@code DiscardPolicy}.
	 */
	public DiscardPolicy() { }

	/**
	 * Does nothing, which has the effect of discarding task r.
	 *
	 * @param r the runnable task requested to be executed
	 * @param e the executor attempting to execute this task
	 */
	public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
	}
}
  1. DiscardOldestPolicy

丢弃队列中最早进入队列的任务,然后再次尝试执行这次提交进线程池的任务

代码语言:javascript
复制
/**
 * A handler for rejected tasks that discards the oldest unhandled
 * request and then retries {@code execute}, unless the executor
 * is shut down, in which case the task is discarded.
 */
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
	/**
	 * Creates a {@code DiscardOldestPolicy} for the given executor.
	 */
	public DiscardOldestPolicy() { }

	/**
	 * Obtains and ignores the next task that the executor
	 * would otherwise execute, if one is immediately available,
	 * and then retries execution of task r, unless the executor
	 * is shut down, in which case task r is instead discarded.
	 *
	 * @param r the runnable task requested to be executed
	 * @param e the executor attempting to execute this task
	 */
	public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
		if (!e.isShutdown()) {
			e.getQueue().poll();
			e.execute(r);
		}
	}
}

线程池执行原理图

家庭作业

实现自己的线程池——扩展ThreadPoolExecutor线程池

  1. 为你的线程池实现分布式全局唯一的线程ID(可以使用Redis,Zookeeper,雪花算法等外部组件)
  2. 线程池的拒绝策略——使用Redis或者MQ,存放到一个分布式消息队列中
  3. 实现线程池的监控,实时观测线程池的核心线程,非核心线程,阻塞队列,拒绝任务数等功能
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 落叶飞翔的蜗牛 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • corePoolSize核心线程
  • 阻塞队列
  • maximumPoolSize最大线程
  • 线程工厂
  • 拒绝策略/饱和策略
  • 线程池执行原理图
  • 家庭作业
    • 实现自己的线程池——扩展ThreadPoolExecutor线程池
    相关产品与服务
    云数据库 Redis
    腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档