2.Java面试题之线程池
创始人
2024-11-15 02:03:08
0

1. 写在前面

线程池是一种管理线程的机制,通过预先创建一定数量的线程,可以在需要时重复使用这些线程,从而避免频繁创建和销毁线程带来的性能开销。

使用线程池的优点包括:

  • 提高性能:线程池减少了线程创建和销毁的开销。
  • 资源管理:线程池控制了线程的数量,避免系统资源耗尽。
  • 任务管理:线程池提供了任务队列,可以管理和调度任务的执行。
  • 简化编程:线程池提供了一些高级功能,如定时任务、周期性任务等,简化了并发编程。

2. Java 中如何创建线程池?

Java 提供了 java.util.concurrent 包中的 Executors 工具类来创建各种类型的线程池。常见的方法包括:

2.1 固定大小线程池

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10); 

2.2 缓存线程池

ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); 

2.3 单线程池

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); 

2.4 定时线程池

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); 

3. 如何提交任务到线程池?

可以通过 execute 方法或 submit 方法将任务提交到线程池。

3.1 使用 execute 方法

fixedThreadPool.execute(new Runnable() {     @Override     public void run() {         System.out.println("Task executed");     } }); 

3.2 使用 submit 方法

Future future = fixedThreadPool.submit(new Callable() {     @Override     public String call() throws Exception {         return "Task completed";     } });  try {     String result = future.get();     System.out.println(result); } catch (InterruptedException | ExecutionException e) {     e.printStackTrace(); } 

4. 什么是 Future 和 Callable?

Callable 是一个类似于 Runnable 的接口,但它可以返回一个结果或抛出异常。Future 表示一个异步计算的结果,可以用来获取 Callable 的返回值或检查任务是否完成。

Callable callableTask = new Callable() {     @Override     public String call() throws Exception {         return "Task result";     } };  Future future = fixedThreadPool.submit(callableTask);  try {     String result = future.get();  // 阻塞等待任务完成并获取结果     System.out.println(result); } catch (InterruptedException | ExecutionException e) {     e.printStackTrace(); } 

5. 如何优雅地关闭线程池?

可以使用 shutdown 或 shutdownNow 方法来关闭线程池。

5.1 shutdown 方法

停止接受新任务,并让已提交的任务执行完毕。

fixedThreadPool.shutdown(); 

5.2 shutdownNow 方法

尝试停止所有正在执行的任务,并返回等待执行的任务列表。

List notExecutedTasks = fixedThreadPool.shutdownNow(); 

5.3 等待线程池关闭

try {     if (!fixedThreadPool.awaitTermination(60, TimeUnit.SECONDS)) {         fixedThreadPool.shutdownNow();     } } catch (InterruptedException e) {     fixedThreadPool.shutdownNow(); } 

6. 线程池的核心参数有哪些?

ThreadPoolExecutor 是 Java 线程池的核心实现类,它的构造函数包含以下参数:

  • corePoolSize:核心线程数,即线程池中始终保持的线程数量。
  • maximumPoolSize:最大线程数,即线程池中允许的最大线程数量。
  • keepAliveTime:非核心线程的空闲时间,超过这个时间将被终止。
  • unit:空闲时间的时间单位。
  • workQueue:任务队列,用于存储等待执行的任务。
  • threadFactory:线程工厂,用于创建新线程。
  • handler:拒绝策略,当任务无法执行时的处理方式。
    5,  // corePoolSize     10, // maximumPoolSize     60, // keepAliveTime     TimeUnit.SECONDS, // unit     new LinkedBlockingQueue(), // workQueue     Executors.defaultThreadFactory(), // threadFactory     new ThreadPoolExecutor.AbortPolicy() // handler ); 

7. 线程池的拒绝策略有哪些?

当线程池无法接受新任务时,会使用拒绝策略来处理这些任务。ThreadPoolExecutor 提供了以下几种拒绝策略:

7.1 AbortPolicy

默认策略,抛出 RejectedExecutionException

new ThreadPoolExecutor.AbortPolicy(); 

7.2 CallerRunsPolicy

由调用线程执行任务

new ThreadPoolExecutor.CallerRunsPolicy(); 

7.3 DiscardPolicy

直接丢弃任务,不抛出异常。

new ThreadPoolExecutor.DiscardPolicy(); 

7.4 DiscardOldestPolicy

丢弃队列中最旧的任务,然后尝试重新提交任务。

new ThreadPoolExecutor.DiscardOldestPolicy(); 

8. 什么是 ForkJoinPool?它与 ThreadPoolExecutor 有什么区别?

ForkJoinPool 是 Java 7 引入的一种特殊的线程池,设计用于处理可以递归拆分成更小任务的并行计算。它基于工作窃取算法,适合处理大规模并行任务。
ForkJoinPool 与 ThreadPoolExecutor 的主要区别在于:

  • 任务类型:ForkJoinPool 适用于可以分解的任务,而 ThreadPoolExecutor 适用于独立的任务。
  • 工作窃取:ForkJoinPool 使用工作窃取算法,提高了多核 CPU 的利用率,而 ThreadPoolExecutor 使用固定的任务队列。
  • API:ForkJoinPool 使用 ForkJoinTask(包括 RecursiveTask 和 RecursiveAction)来表示任务,而 ThreadPoolExecutor 使用 Runnable 和 Callable。
import java.util.concurrent.RecursiveTask; import java.util.concurrent.ForkJoinPool;  class SumTask extends RecursiveTask {     private final int[] array;     private final int start, end;      public SumTask(int[] array, int start, int end) {         this.array = array;         this.start = start;         this.end = end;     }      @Override     protected Integer compute() {         if (end - start <= 10) {             int sum = 0;             for (int i = start; i < end; i++) {                 sum += array[i];             }             return sum;         } else {             int mid = (start + end) / 2;             SumTask leftTask = new SumTask(array, start, mid);             SumTask rightTask = new SumTask(array, mid, end);             leftTask.fork();             return rightTask.compute() + leftTask.join();         }     } }  public class ForkJoinExample {     public static void main(String[] args) {         int[] array = new int[100];         for (int i = 0; i < array.length; i++) {             array[i] = i + 1;         }          ForkJoinPool pool = new ForkJoinPool();         SumTask task = new SumTask(array, 0, array.length);         int result = pool.invoke(task);         System.out.println("Sum: " + result);     } } 

9. 如何自定义线程池的拒绝策略?

在 ThreadPoolExecutor 中,当线程池和队列都满时,可以自定义拒绝策略来处理新提交的任务。可以通过实现 RejectedExecutionHandler 接口来自定义拒绝策略。

import java.util.concurrent.*;  public class CustomRejectedExecutionHandler implements RejectedExecutionHandler {     @Override     public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {         // 自定义拒绝策略,例如记录日志或将任务放入另一个队列         System.out.println("Task " + r.toString() + " rejected from " + executor.toString());     }      public static void main(String[] args) {         ThreadPoolExecutor executor = new ThreadPoolExecutor(             2, 4, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2),             Executors.defaultThreadFactory(),             new CustomRejectedExecutionHandler()         );          for (int i = 0; i < 10; i++) {             executor.execute(() -> {                 try {                     Thread.sleep(1000);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 System.out.println(Thread.currentThread().getName() + " is executing task");             });         }          executor.shutdown();     } } 

10. 如何实现一个自定义的线程池?

可以通过继承 ThreadPoolExecutor 类来实现一个自定义的线程池,并重写其方法以添加自定义行为。

import java.util.concurrent.*;  public class CustomThreadPoolExecutor extends ThreadPoolExecutor {     public CustomThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue workQueue) {         super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);     }      @Override     protected void beforeExecute(Thread t, Runnable r) {         super.beforeExecute(t, r);         System.out.println("Before executing task: " + r.toString());     }      @Override     protected void afterExecute(Runnable r, Throwable t) {         super.afterExecute(r, t);         System.out.println("After executing task: " + r.toString());     }      @Override     protected void terminated() {         super.terminated();         System.out.println("Thread pool terminated");     }      public static void main(String[] args) {         CustomThreadPoolExecutor executor = new CustomThreadPoolExecutor(             2, 4, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2)         );          for (int i = 0; i < 10; i++) {             executor.execute(() -> {                 try {                     Thread.sleep(1000);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 System.out.println(Thread.currentThread().getName() + " is executing task");             });         }          executor.shutdown();     } } 

11. 如何实现一个具有动态调整功能的线程池?

可以通过定期调整 ThreadPoolExecutor 的核心参数来实现动态调整线程池的功能。例如,可以使用 ScheduledExecutorService 定期检查任务队列的长度,并根据需要调整核心线程数和最大线程数。

import java.util.concurrent.*;  public class DynamicThreadPool {     public static void main(String[] args) {         ThreadPoolExecutor executor = new ThreadPoolExecutor(             2, 4, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<>(2)         );          ScheduledExecutorService adjuster = Executors.newScheduledThreadPool(1);         adjuster.scheduleAtFixedRate(() -> {             int queueSize = executor.getQueue().size();             if (queueSize > 2) {                 executor.setCorePoolSize(Math.min(executor.getCorePoolSize() + 1, 10));                 executor.setMaximumPoolSize(Math.min(executor.getMaximumPoolSize() + 1, 20));             } else if (queueSize == 0) {                 executor.setCorePoolSize(Math.max(executor.getCorePoolSize() - 1, 2));                 executor.setMaximumPoolSize(Math.max(executor.getMaximumPoolSize() - 1, 4));             }             System.out.println("Adjusted Pool Size: " + executor.getCorePoolSize() + ", " + executor.getMaximumPoolSize());         }, 0, 1, TimeUnit.SECONDS);          for (int i = 0; i < 10; i++) {             executor.execute(() -> {                 try {                     Thread.sleep(1000);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 System.out.println(Thread.currentThread().getName() + " is executing task");             });         }          executor.shutdown();         try {             executor.awaitTermination(10, TimeUnit.SECONDS);         } catch (InterruptedException e) {             e.printStackTrace();         }         adjuster.shutdown();     } } 

12. 如何实现一个线程池的优先级队列?

可以使用 PriorityBlockingQueue 来实现一个具有优先级的线程池。任务需要实现 Comparable 接口,以定义任务的优先级。

import java.util.concurrent.*;  public class PriorityThreadPool {     static class PriorityTask implements Runnable, Comparable {         private final int priority;         private final String name;          public PriorityTask(int priority, String name) {             this.priority = priority;             this.name = name;         }          @Override         public void run() {             System.out.println(Thread.currentThread().getName() + " is executing task: " + name);         }          @Override         public int compareTo(PriorityTask o) {             return Integer.compare(o.priority, this.priority);         }          @Override         public String toString() {             return name + "(priority=" + priority + ")";         }     }      public static void main(String[] args) {         ThreadPoolExecutor executor = new ThreadPoolExecutor(             2, 4, 60, TimeUnit.SECONDS, new PriorityBlockingQueue<>()         );          for (int i = 0; i < 10; i++) {             int priority = i % 3;             executor.execute(new PriorityTask(priority, "Task-" + i));         }          executor.shutdown();     } } 

13. 如何实现一个支持超时任务的线程池?

可以使用 ScheduledThreadPoolExecutor 来实现一个支持超时任务的线程池。通过 schedule 方法,可以提交一个带有超时功能的任务。

import java.util.concurrent.*;  public class TimeoutThreadPool {     public static void main(String[] args) {         ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(4);          for (int i = 0; i < 10; i++) {             ScheduledFuture future = executor.schedule(() -> {                 try {                     Thread.sleep(2000);                 } catch (InterruptedException e) {                     e.printStackTrace();                 }                 System.out.println(Thread.currentThread().getName() + " is executing task");             }, 1, TimeUnit.SECONDS);              executor.schedule(() -> {                 if (!future.isDone()) {                     future.cancel(true);                     System.out.println("Task timed out and was cancelled");                 }             }, 3, TimeUnit.SECONDS);         }          executor.shutdown();     } } 

相关内容

热门资讯

专业讨论!德扑之星真破解套路(... 专业讨论!德扑之星真破解套路(辅助挂)软件透明挂(有挂了解)-哔哩哔哩;人气非常高,ai更新快且高清...
每日必看!智星德州菠萝外挂检测... 每日必看!智星德州菠萝外挂检测(辅助挂)软件透明挂(有挂教学)-哔哩哔哩1、玩家可以在智星德州菠萝外...
透视透明挂!轰趴十三水有后台(... 轰趴十三水有后台赢率提升策略‌;透视透明挂!轰趴十三水有后台(辅助挂)软件透明挂(有挂详情)-哔哩哔...
发现玩家!德扑ai助手软件(辅... 发现玩家!德扑ai助手软件(辅助挂)透视辅助(有挂教学)-哔哩哔哩;玩家在德扑ai助手软件中需先进行...
一分钟了解!x-poker辅助... 一分钟了解!x-poker辅助软件(辅助挂)辅助透视(有挂攻略)-哔哩哔哩1、每一步都需要思考,不同...
一分钟揭秘!德州最新辅助器(辅... 一分钟揭秘!德州最新辅助器(辅助挂)透视辅助(有挂攻略)-哔哩哔哩;德州最新辅助器最新版本免费下载安...
玩家攻略推荐!德州辅助(辅助挂... 玩家攻略推荐!德州辅助(辅助挂)辅助透视(有挂了解)-哔哩哔哩是由北京得德州辅助黑科技有限公司精心研...
揭秘真相!pokernow德州... 《揭秘真相!pokernow德州(辅助挂)辅助透视(有挂介绍)-哔哩哔哩》 pokernow德州软件...
五分钟了解!德州之星辅助器(辅... 五分钟了解!德州之星辅助器(辅助挂)辅助透视(有挂透明)-哔哩哔哩1、很好的工具软件,可以解锁游戏的...
推荐一款!pokermaste... 1、推荐一款!pokermaster有外挂(辅助挂)透视辅助(有挂教学)-哔哩哔哩;详细教程。2、p...