MST

星途 面试题库

面试题:Java线程池的拒绝策略及其应用场景

Java线程池中有哪些内置的拒绝策略?请分别说明它们的特点,并举例阐述在何种实际业务场景下会使用到不同的拒绝策略。
17.4万 热度难度
编程语言Java

知识考点

AI 面试

面试题答案

一键面试
  1. AbortPolicy(中止策略)
    • 特点:当任务添加到线程池被拒绝时,会抛出RejectedExecutionException异常。
    • 场景:在一些对任务执行成功率要求极高,不允许任务丢失的场景中使用。例如,银行转账操作,每一个转账任务都必须确保执行,若任务被拒绝就抛出异常,方便开发者及时处理,避免转账丢失。代码示例:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class AbortPolicyExample {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, 
                2, 
                0L, 
                TimeUnit.MILLISECONDS, 
                new ArrayBlockingQueue<>(1), 
                new ThreadPoolExecutor.AbortPolicy());
        for (int i = 0; i < 4; i++) {
            int taskNumber = i;
            executor.submit(() -> {
                System.out.println("Task " + taskNumber + " is running on thread " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executor.shutdown();
    }
}
  1. CallerRunsPolicy(调用者运行策略)
    • 特点:当任务添加到线程池被拒绝时,该任务会被交给调用execute方法的线程来执行。这样做的好处是不会抛弃任务,也不会抛出异常,但是会影响调用者的性能。
    • 场景:适用于处理一些对响应时间要求不是特别高,但又不希望任务被丢弃的场景。比如,在一个简单的日志记录系统中,若线程池满了,由调用记录日志的主线程来执行日志记录任务,虽然可能会稍微影响主线程性能,但能保证日志记录不会丢失。代码示例:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class CallerRunsPolicyExample {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, 
                2, 
                0L, 
                TimeUnit.MILLISECONDS, 
                new ArrayBlockingQueue<>(1), 
                new ThreadPoolExecutor.CallerRunsPolicy());
        for (int i = 0; i < 4; i++) {
            int taskNumber = i;
            executor.submit(() -> {
                System.out.println("Task " + taskNumber + " is running on thread " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executor.shutdown();
    }
}
  1. DiscardPolicy(丢弃策略)
    • 特点:当任务添加到线程池被拒绝时,直接默默丢弃该任务,不做任何处理。
    • 场景:在一些对任务执行结果不是特别关心,且任务量较大的场景下使用。例如,在一个高并发的访问统计系统中,偶尔丢失一些访问记录任务对整体统计结果影响不大,为了避免任务过多导致系统资源耗尽,可以采用此策略。代码示例:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class DiscardPolicyExample {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, 
                2, 
                0L, 
                TimeUnit.MILLISECONDS, 
                new ArrayBlockingQueue<>(1), 
                new ThreadPoolExecutor.DiscardPolicy());
        for (int i = 0; i < 4; i++) {
            int taskNumber = i;
            executor.submit(() -> {
                System.out.println("Task " + taskNumber + " is running on thread " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executor.shutdown();
    }
}
  1. DiscardOldestPolicy(丢弃最旧策略)
    • 特点:当任务添加到线程池被拒绝时,会丢弃队列中最老的一个任务(即最先进入队列的任务),然后尝试把新任务加入队列。
    • 场景:适用于任务执行时间较短,队列中任务可以被适当替换的场景。比如,在一个实时行情数据更新系统中,新的行情数据任务不断进来,如果线程池满了,丢弃最早的行情数据任务,让新的任务进入,以保证系统处理的是相对较新的数据。代码示例:
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class DiscardOldestPolicyExample {
    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, 
                2, 
                0L, 
                TimeUnit.MILLISECONDS, 
                new ArrayBlockingQueue<>(1), 
                new ThreadPoolExecutor.DiscardOldestPolicy());
        for (int i = 0; i < 4; i++) {
            int taskNumber = i;
            executor.submit(() -> {
                System.out.println("Task " + taskNumber + " is running on thread " + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }
        executor.shutdown();
    }
}