在 Java 中,Executors 是一个工具类,它提供了用于创建和管理线程池的一些便捷方法。Executors 类位于 java.util.concurrent 包中,主要用于简化 ThreadPoolExecutor 的创建过程,为开发者提供了几种常用的线程池实现,如固定大小线程池、单线程池、缓存线程池和定时调度线程池等。
1. 什么是 Executors 类?
Executors 类是一个工厂类,它提供了一些静态工厂方法来创建不同类型的线程池。使用 Executors 类可以非常方便地创建和管理线程池,而不需要手动配置 ThreadPoolExecutor 的参数。它可以帮助开发者快速创建适合特定需求的线程池。
Executors 通过以下静态方法来提供线程池的创建:
newFixedThreadPool(int nThreads):创建一个固定大小的线程池。newCachedThreadPool():创建一个可以根据需要创建新线程的线程池。newSingleThreadExecutor():创建一个只有一个线程的线程池。newScheduledThreadPool(int corePoolSize):创建一个支持定时和周期性任务执行的线程池。
2. Executors 提供的常用线程池
Executors 提供的几种常用线程池涵盖了不同的应用场景,下面是它们的详细介绍:
2.1 newFixedThreadPool(int nThreads)
newFixedThreadPool(int nThreads) 方法创建一个固定大小的线程池。该线程池中的线程数始终固定,当有任务提交时,如果线程池中的线程都在执行任务,新的任务将会进入等待队列,直到有空闲线程可以执行任务。
-
特点:
- 线程池的线程数固定,避免了频繁创建和销毁线程的开销。
- 适用于负载较重的服务器环境,可以通过限制线程数量来保证系统的稳定性。
- 任务队列采用无界队列(LinkedBlockingQueue),不会拒绝任务。 -
使用场景:适用于执行长期任务或控制线程数量的场景,如服务器处理请求时限制最大并发线程数。
-
示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class FixedThreadPoolExample {
public static void main(String[] args) {
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3); // 创建一个固定大小为3的线程池
for (int i = 1; i <= 5; i++) {
final int taskId = i;
fixedThreadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 执行任务 " + taskId);
try {
Thread.sleep(2000); // 模拟任务执行耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
fixedThreadPool.shutdown(); // 关闭线程池
}
}
2.2 newCachedThreadPool()
newCachedThreadPool() 方法创建一个可以根据需要创建新线程的线程池。该线程池会根据任务的需要来动态创建线程,线程的数量几乎没有限制(取决于 JVM 和系统资源)。当任务执行完毕后,空闲线程会被缓存,如果线程空闲时间超过 60 秒,则会被终止并移出缓存。
-
特点:
- 线程池大小不固定,完全依赖任务的执行情况来决定线程数量。
- 适合执行大量短期的异步任务。
- 线程空闲时间超过 60 秒会被回收,因此不会占用系统过多资源。 -
使用场景:适用于短时间大量任务执行并且希望能快速释放资源的场景。
-
示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class CachedThreadPoolExample {
public static void main(String[] args) {
ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); // 创建一个可缓存的线程池
for (int i = 1; i <= 5; i++) {
final int taskId = i;
cachedThreadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 执行任务 " + taskId);
try {
Thread.sleep(2000); // 模拟任务执行耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
cachedThreadPool.shutdown(); // 关闭线程池
}
}
2.3 newSingleThreadExecutor()
newSingleThreadExecutor() 方法创建一个只有一个线程的线程池。该线程池只有一个工作线程来执行任务,所有任务按照顺序执行,保证任务在同一个线程中被按顺序执行。
-
特点:
- 线程池只有一个工作线程,可以保证所有任务按照顺序执行。
- 当唯一的工作线程意外终止时,会创建一个新的线程继续执行任务。
- 任务队列是无界的,所有提交的任务会依次进入队列等待执行。 -
使用场景:适用于需要按照顺序执行任务的场景,如需要同步文件或数据库操作。
-
示例代码:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SingleThreadExecutorExample {
public static void main(String[] args) {
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); // 创建一个单线程的线程池
for (int i = 1; i <= 5; i++) {
final int taskId = i;
singleThreadExecutor.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 执行任务 " + taskId);
try {
Thread.sleep(2000); // 模拟任务执行耗时
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
singleThreadExecutor.shutdown(); // 关闭线程池
}
}
2.4 newScheduledThreadPool(int corePoolSize)
newScheduledThreadPool(int corePoolSize) 方法创建一个支持定时和周期性任务执行的线程池。该线程池适用于需要定时或周期性执行任务的场景,如调度系统或定时任务。
-
特点:
- 可以设置核心线程池的大小(corePoolSize)。
- 支持定时任务和周期性任务执行。
- 使用ScheduledExecutorService接口的实现类,可以使用schedule()方法提交定时任务,使用scheduleAtFixedRate()或scheduleWithFixedDelay()方法提交周期性任务。 -
使用场景:适用于需要周期性执行任务或在指定时间点执行任务的场景。
-
示例代码:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class ScheduledThreadPoolExample {
public static void main(String[] args) {
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2); // 创建一个支持定时和周期性任务的线程池
// 提交一个定时任务,延迟 3 秒后执行
scheduledThreadPool.schedule(() -> {
System.out.println(Thread.currentThread().getName() + " 执行定时任务");
}, 3, TimeUnit.SECONDS);
// 提交一个周期性任务,延迟 1 秒后开始执行,每隔 2 秒执行一次
scheduledThreadPool.scheduleAtFixedRate(() -> {
System.out.println(Thread.currentThread().getName() + " 执行周期性任务");
}, 1, 2, TimeUnit.SECONDS);
// 提交一个周期性任务,延迟 1 秒后开始执行,当上一个任务执行完毕后,等待 2 秒再执行
scheduledThreadPool.scheduleWithFixedDelay(() -> {
System.out.println(Thread.currentThread().getName() + " 执行延迟周期性任务");
}, 1, 2, TimeUnit.SECONDS);
}
}
3. 使用 Executors 创建线程池的优点
-
简化线程池的创建:
Executors提供了各种静态工厂方法,使得线程池的创建和使用更加简单方便,避免了手动配置ThreadPoolExecutor的麻烦。 -
提高代码的可读性:通过使用
Executors提供的方法,代码更加简洁,易于理解和维护。 -
避免手动管理线程:使用线程池后,开发者不再需要手动管理线程的创建和销毁,提高了资源的利用率,降低了系统的开销。
4. 使用 Executors 的注意事项
-
合理选择线程池类型:根据具体的应用场景选择合适的线程池类型,以避免资源浪费和潜在的性能问题。
-
避免使用无界队列:在实际使用中,应避免使用无界队列(如
LinkedBlockingQueue),因为它可能会导致内存溢出。可以设置一个合理的队列
大小来控制任务的积压。
-
使用
shutdown()方法关闭线程池:当线程池不再使用时,应该调用shutdown()或shutdownNow()方法来关闭线程池,避免资源泄漏。 -
注意异常处理:在线程池执行任务时,应做好异常处理,防止未捕获的异常导致线程池中的线程终止。
-
不要使用
Executors创建的默认线程池:Executors创建的默认线程池(如FixedThreadPool、CachedThreadPool)在某些情况下可能存在资源管理问题,如无限队列导致的内存泄漏。因此,建议使用ThreadPoolExecutor来显式地创建线程池,并合理配置线程池参数。
5. 总结
Executors 是 Java 并发编程中的一个工具类,为开发者提供了一些便捷的方法来创建和管理线程池。通过 Executors,开发者可以轻松创建不同类型的线程池(如固定大小线程池、缓存线程池、单线程池和定时调度线程池),并根据实际应用场景选择合适的线程池类型。
1882

被折叠的 条评论
为什么被折叠?



