Java面试题:线程池面试题

封面:线程池专题.png

王有志,一个分享硬核 Java 技术的互金摸鱼侠
加入 Java 人的提桶跑路群:共同富裕的Java人

今天是《面霸的自我修养》第 6 篇文章,我们一起来看看面试中会问到哪些关于线程池的问题吧。
数据来源

  • 大部分来自于各机构(Java 之父,Java 继父,某灵,某泡,某客)以及各博主整理文档;
  • 小部分来自于我以及身边朋友的实际经历,题目上会做出标识,并注明面试公司。

叠“BUFF”

  • 八股文通常出现在面试的第一二轮,是“敲门砖”,但仅仅掌握八股文并不能帮助你拿下 Offer;
  • 由于本人水平有限,文中难免出现错误,还请大家以批评指正为主,尽量不要喷~~

线程池是什么?为什么要使用线程池?

难易程度:🔥🔥

重要程度:🔥🔥🔥🔥🔥

面试公司:无

计算机中,线程的创建和销毁开销较大,频繁的创建和销毁线程会影响程序性能。利用基于池化思想的线程池来统一管理和分配线程,复用已创建的线程,避免频繁创建和销毁线程带来的资源消耗,提高系统资源的利用率
线程池具有以下 3 点优势:

  • 降低资源消耗,重复利用已经创建的线程,避免线程创建与销毁带来的资源消耗;
  • 提高响应速度,接收任务时,可以通过线程池直接获取线程,避免了创建线程带来的时间消耗;
  • 便于管理线程,统一管理和分配线程,避免无限制创建线程,另外可以引入线程监控机制。

Java 中如何创建线程池?

难易程度:🔥🔥

重要程度:🔥🔥🔥🔥

面试公司:无

Java 中可以通过 ThreadPoolExecutor 和 Executors 创建线程池。

使用 ThreadPoolExecutor 创建线程池

使用 ThreadPoolExecutor 可以创建自定义线程池,例如:

ExecutorService threadPoolExecutor = new ThreadPoolExecutor(
	10, 
	20, 
	10, 
	TimeUnit.MILLISECONDS, 
	new LinkedBlockingQueue<Runnable>(),
	new ThreadPoolExecutor.AbortPolicy()
);

了解以上代码的含义前,我们先来看 ThreadPoolExecutor 提供的的构造方法:

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
   
	this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
   
	this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, defaultHandler);
}

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
   
	this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), handler);
}

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
   
	if (corePoolSize < 0 || maximumPoolSize <= 0 || maximumPoolSize < corePoolSize || keepAliveTime < 0)
		throw new IllegalArgumentException();
	if (workQueue == null || threadFactory == null || handler == null)
		throw new NullPointerException();
	this.corePoolSize = corePoolSize;
	this.maximumPoolSize = maximumPoolSize;
	this.workQueue = workQueue;
	this.keepAliveTime = unit.toNanos(keepAliveTime);
	this.threadFactory = threadFactory;
	this.handler = handler;
}

ThreadPoolExecutor 提供了 4 个构造方法,但最后都会指向含有 7 个参数的构造方法上。我们一一说明这些参数的含义:

  • int corePoolSize,线程池的核心线程数量,核心线程在线程池的生命周期中不会被销毁
  • int maximumPoolSize,线程池的最大线程数量,超出核心线程数量的非核心线程
  • long keepAliveTime,线程存活时间,非核心线程空闲时存活的最大时间;
  • TimeUnit unit,keepAliveTime 的时间单位;
  • BlockingQueue<Runnable> workQueue,线程池的任务队列;
  • ThreadFactory threadFactory,线程工厂,用于创建线程,可以自定义线程;
  • RejectedExecutionHandler handler,拒绝策略,当任务数量超出线程池的容量(超过 maximumPoolSize 并且 workQueue 已满)时的处理策略。

使用 Executors 创建线程池

除了使用 ThreadPoolExecutor 创建线程池外,还可以通过 Executors 创建 Java 内置的线程池,Java 中提供了 6 种内置线程池:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

ExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);

ExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();

ExecutorService workStealingPool = Executors.newWorkStealingPool();

Tips:关于这 6 种线程池的详细解释,参考下一题。


Java 中提供了哪些线程池?

难易程度:🔥🔥

重要程度:🔥🔥🔥🔥

面试公司:无

Java 中提供了 6 种线程池,可以通过 Executors 获取。

FixedThreadPool

通过 Executors 创建 FixedThreadPool 的代码如下:

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);

FixedThreadPool 是固定线程数量的线程池,通过Executors#newFixedThreadPool方法获得,源码如下:

public static ExecutorService newFixedThreadPool(int nThreads) {
   
	return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}

本质上是通过 ThreadPoolExecutor 创建的线程池,核心线程数和最大线程数相同,工作队列使用 LinkedBlockingQueue,该队列最大容量是 Integer.MAX_VALUE

SingleThreadExecutor

通过 Executors 创建 SingleThreadExecutor 的代码如下:

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

SingleThreadExecutor 是只有一个线程的线程池,通过Executors#newSingleThreadExecutor方法获得,其源码如下:

public static ExecutorService newSingleThreadExecutor() {
   
	return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()));
}

依旧是通过 ThreadPoolExecutor 创建的线程池,最大线程数和核心线程数设置为 1,工作队列使用 LinkedBlockingQueue。SingleThreadExecutor 适合按顺序执行的场景。

ScheduledThreadPool

通过 Executors 创建 ScheduledThreadPool 的代码如下:

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(10);

ScheduledThreadPool 是具有定时调度和延迟调度能力的线程池,通过Executors#newScheduledThreadPool方法获得,其源码如下:

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
   
	return new ScheduledThreadPoolExecutor(corePoolSize);
}

与前两个不同的是 ScheduledThreadPool 是用过 ScheduledThreadPoolExecutor 创建的,源码如下:

public class Executors {
   
	public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
   
		return new ScheduledThreadPoolExecutor(corePoolSize);
	}
}

public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {
   
	public ScheduledThreadPoolExecutor(int corePoolSize) {
   
		super(corePoolSize, Integer.MAX_VALUE, DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS, new DelayedWorkQueue());
	}
}

追根溯源的话 ScheduledThreadPoolExecutor 依旧是通过 ThreadPoolExecutor 的构造方法创建线程池的,能够实现定时调度的特性是因为ScheduledThreadPoolExecutor#execute方法和ScheduledThreadPoolExecutor#schedule方法实现的:

public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {
   
	public void execute(Runnable command) {
   
		schedule(command, 0, NANOSECONDS);
	}

	public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit) {
   
		if (command == null || unit == null)
			throw new NullPointerException();
		RunnableScheduledFuture<Void> t = decorateTask(command, new ScheduledFutureTask<Void>(command, null, triggerTime(delay, unit), sequencer.getAndIncrement()));
		delayedExecute(t);
		return t;
	}
}

因为 ScheduledThreadPoolExecutor 并不是线程池中的重点内容,这里我们不过多讨论源码的实现,我们接下来看 ScheduledThreadPoolExecutor 该如何使用:

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println("当前时间:" + simpleDateFormat.format(new Date()));

scheduledThreadPool.schedule(new Runnable() {
   
	@Override
	public void run() {
   
		System.out.println("执行时间:" + simpleDateFormat.format(new Date()) + ",延迟3秒执行");
	}
}, 3, TimeUnit.SECONDS);

scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
   
	@Override
	public void run() {
   
		System.out.println("执行时间:" + simpleDateFormat.format(new Date()) + ",每3秒执行一次");
	}
}, 0, 3, TimeUnit.SECONDS);

SingleThreadScheduledExecutor

通过 Executors 创建 ScheduledExecutorService 的代码如下:

ExecutorService singleThreadScheduledExecutor = Executors.newSingleThreadScheduledExecutor();

与 ScheduledThreadPool 相同 SingleThreadScheduledExecutor 也是具有定时调度和延迟调度能力的线程池,同样的 SingleThreadScheduledExecutor 也是通过 ScheduledThreadPoolExecutor 创建的,不同之处在于 ScheduledThreadPool 并不限制核心线程的数量,而 SingleThreadScheduledExecutor 只会创建一个核心线程

CachedThreadPool

通过 Executors 创建 CachedThreadPool 的代码如下:

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

CachedThreadPool 是可缓存线程的线程池,通过Executors#newSingleThreadExecutor方法获得,其源码如下:

public static ExecutorService newCachedThreadPool() 
  • 28
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
线程池Java中用于管理和复用线程的机制,它可以提高线程的利用率和性能。线程池的七大参数包括: 1. corePoolSize(核心线程数):线程池中始终保持的线程数量,即使它们处于空闲状态。当任务数量超过核心线程数时,线程池会创建新的线程来处理任务。 2. maximumPoolSize(最大线程数):线程池中允许存在的最大线程数量。当任务数量超过最大线程数时,新的任务会被放入等待队列中等待执行。 3. keepAliveTime(线程空闲时间):当线程池中的线程数量超过核心线程数时,多余的空闲线程在等待新任务到来时的最长等待时间。超过这个时间,空闲线程会被销毁。 4. unit(时间单位):用于设置keepAliveTime的时间单位,可以是秒、毫秒、分钟等。 5. workQueue(任务队列):用于存放等待执行的任务的队列。常见的队列类型有有界队列(如ArrayBlockingQueue)和无界队列(如LinkedBlockingQueue)。 6. threadFactory(线程工厂):用于创建新线程的工厂类。可以自定义线程的名称、优先级等属性。 7. handler(拒绝策略):当任务无法被线程池执行时的处理策略。常见的策略有直接抛出异常、丢弃任务、丢弃队列中最旧的任务等。 线程池的作用主要有以下几点: 1. 提高性能:线程池可以复用线程,避免了频繁创建和销毁线程的开销,提高了系统的性能。 2. 控制资源:通过设置核心线程数和最大线程数,可以控制系统中并发线程的数量,避免资源被过度占用。 3. 提供任务队列:线程池可以提供一个任务队列,用于存放等待执行的任务。当线程池中的线程都在执行任务时,新的任务会被放入队列中等待执行。 4. 管理线程:线程池可以统一管理线程的生命周期,包括创建、销毁、空闲时间等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术范王有志

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值