最近面试问道了线程池的创建方式,这里出一篇文章记录下这一知识点!
线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务。线程池线程都是后台线程。每个线程都使用默认的ThreadFactory创建一个新线程。
线程池的创建主要依赖于java.util.concurrent
包下的ExecutorService
和Executors
类。最近面试问到了这块,所以这篇文章我们将详细介绍几种常见的线程池创建方式。
FixedThreadPool
是一种固定大小的线程池。它的核心线程数和最大线程数都是创建时指定的数值,且不会变化。就算空闲,也不会回收线程,除非设置了allowCoreThreadTimeOut
。
举个栗子:我们创建一个固定大小为5的线程池,并提交10个任务。由于线程池的大小固定为5,因此这5个线程会并发执行,而剩下的任务会等待前面的任务完成后再执行。
代码如下:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPoolDemo {
public static void main(String[] args) {
// 创建一个固定大小的线程池
ExecutorService executorService = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread("" + i);
executorService.execute(worker); // 执行任务
}
executorService.shutdown(); // 关闭线程池
while (!executorService.isTerminated()) {
}
System.out.println("所有任务已完成");
}
}
class WorkerThread implements Runnable {
private String command;
public WorkerThread(String s) {
this.command = s;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 开始. 命令 = " + command);
processCommand();
System.out.println(Thread.currentThread().getName() + " 结束.");
}
private void processCommand() {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
CachedThreadPool
是一种缓存线程池,它的线程数量是不定的,可以说是几乎无限的。当提交一个任务时,如果线程池中有空闲线程,则立即执行;如果没有,则创建一个新线程执行。当线程空闲超过60秒,则自动回收。
举个栗子:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPoolDemo {
public static void main(String[] args) {
// 创建一个可缓存的线程池
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread("" + i);
executorService.execute(worker); // 执行任务
}
executorService.shutdown(); // 关闭线程池
while (!executorService.isTerminated()) {
}
System.out.println("所有任务已完成");
}
}
CachedThreadPool`适合执行大量的耗时较少的任务,如Web服务器。
SingleThreadExecutor
是一个单线程的Executor,它使用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束,会有另一个取代它,保证顺序执行。
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SingleThreadExecutorDemo {
public static void main(String[] args) {
// 创建一个单线程的线程池
ExecutorService executorService = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
Runnable worker = new WorkerThread("" + i);
executorService.execute(worker); // 执行任务
}
executorService.shutdown(); // 关闭线程池
while (!executorService.isTerminated()) {
}
System.out.println("所有任务已完成");
}
}
在SingleThreadExecutor
中,由于只有一个线程,因此任务会按照提交的顺序一个接一个地执行,不会出现并发的情况。
ScheduledThreadPool
是 Java 并发包 java.util.concurrent
中提供的一个线程池实现,它用于在给定的延迟后运行命令,或者定期地执行命令。ScheduledThreadPool
的主要特点是能够处理需要定时执行或周期性执行的任务。
举个栗子:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadPoolDemo {
public static void main(String[] args) {
// 创建一个ScheduledThreadPool,包含3个核心线程
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
// 提交一个Runnable任务,在延迟2秒后执行
scheduledExecutorService.schedule(new RunnableTask("一次性任务"), 2, TimeUnit.SECONDS);
// 提交一个Runnable任务,初始延迟2秒,之后每隔1秒执行一次
scheduledExecutorService.scheduleAtFixedRate(new RunnableTask("周期性任务"), 2, 1, TimeUnit.SECONDS);
// 通常我们需要在程序结束时关闭线程池
// scheduledExecutorService.shutdown();
}
static class RunnableTask implements Runnable {
private String command;
public RunnableTask(String command) {
this.command = command;
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " 开始执行命令: " + command + " at " + System.currentTimeMillis());
}
}
}
我们创建了一个 ScheduledThreadPool
,它包含3个核心线程。然后,我们提交了两个任务:
除了以上的用法
,ScheduledExecutorService
提供了几个用于定时执行任务的方法:
schedule(Runnable command, long delay, TimeUnit unit)
:在给定延迟后运行命令一次。scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
:初始延迟后开始首次执行,然后随后每隔固定周期执行一次。scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
:初始延迟后开始首次执行,然后每次执行完毕后等待固定延迟再次执行。如果任务执行过程中抛出异常,那么 ScheduledThreadPool
会停止该任务的后续执行,但不会停止线程池本身或其他任务的执行。如果需要处理任务执行中的异常,可以在任务内部进行捕获和处理。
以上就是创建线程池的几种方式,本篇文章到此结束,谢谢大家的观看!
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。