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

前言

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

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提供的默认线程工厂如下。当然你要是看这个默认的线程工厂不爽,你也可以自定义线程工厂。

/**
 * 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方法的线程)执行任务,线程池本身处于饱和状态时不再受理任何外部提交的任务。

/**
 * 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();
		}
	}
}

2. AbortPolicy

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

/**
 * 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());
	}
}

3. DiscardPolicy

    悄悄地把任务丢弃,不做任何处理和记录
/**
 * 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) {
	}
}

4.DiscardOldestPolicy

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

/**
 * 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.实现线程池的监控,实时观测线程池的核心线程,非核心线程,阻塞队列,拒绝任务数等功能

关注下面二维码,关注公众号。了解学习更多技术知识,领取免费资料。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值