线程池拒绝策略:优雅处理过载请求

开发
当线程池达到最大容量时,会出现任务无法提交的情况,这时需要处理这种情况的机制称为“拒绝策略”(Rejection Policy)。本文将详细讲述 Java 中的线程池拒绝策略,帮助读者理解其原理和应用场景。

什么是线程池拒绝策略?

线程池拒绝策略是指当线程池中的任务数量达到上限时,新提交的任务如何处理的一种策略。Java 提供了几种内置的拒绝策略,开发者也可以自定义策略。

内置的拒绝策略

Java 提供了以下几种内置的拒绝策略:

  • AbortPolicy:默认策略,直接抛出 RejectedExecutionException 异常,阻止系统正常运行。
  • CallerRunsPolicy:由调用线程处理该任务,既不抛弃任务,也不抛出异常。
  • DiscardPolicy:直接丢弃任务,不予处理。
  • DiscardOldestPolicy:丢弃最旧的任务,然后尝试重新提交被拒绝的任务。

下面,我们通过代码示例来详细讲述这些策略的实现和应用。

代码示例

创建一个简单的线程池:

import java.util.concurrent.*;

public class ThreadPoolExample {
    private static final int CORE_POOL_SIZE = 2;
    private static final int MAX_POOL_SIZE = 4;
    private static final long KEEP_ALIVE_TIME = 10L;

    public static void main(String[] args) {
        // 使用 ArrayBlockingQueue 作为任务队列,容量为 2
        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);

        // 创建线程池
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            CORE_POOL_SIZE,
            MAX_POOL_SIZE,
            KEEP_ALIVE_TIME,
            TimeUnit.SECONDS,
            queue
        );

        // 提交任务
        for (int i = 0; i < 10; i++) {
            final int taskNumber = i + 1;
            executor.submit(() -> {
                try {
                    System.out.println("Executing task " + taskNumber);
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        // 关闭线程池
        executor.shutdown();
    }
}

使用 AbortPolicy:

import java.util.concurrent.*;

public class AbortPolicyExample {
    public static void main(String[] args) {
        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 10L, TimeUnit.SECONDS, queue,
            new ThreadPoolExecutor.AbortPolicy() // 使用 AbortPolicy
        );

        for (int i = 0; i < 10; i++) {
            final int taskNumber = i + 1;
            try {
                executor.submit(() -> {
                    try {
                        System.out.println("Executing task " + taskNumber);
                        Thread.sleep(2000);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                });
            } catch (RejectedExecutionException e) {
                System.err.println("Task " + taskNumber + " was rejected");
            }
        }

        executor.shutdown();
    }
}

使用 CallerRunsPolicy:

import java.util.concurrent.*;

public class CallerRunsPolicyExample {
    public static void main(String[] args) {
        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 10L, TimeUnit.SECONDS, queue,
            new ThreadPoolExecutor.CallerRunsPolicy() // 使用 CallerRunsPolicy
        );

        for (int i = 0; i < 10; i++) {
            final int taskNumber = i + 1;
            executor.submit(() -> {
                System.out.println("Executing task " + taskNumber);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        executor.shutdown();
    }
}

使用 DiscardPolicy:

import java.util.concurrent.*;

public class DiscardPolicyExample {
    public static void main(String[] args) {
        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 10L, TimeUnit.SECONDS, queue,
            new ThreadPoolExecutor.DiscardPolicy() // 使用 DiscardPolicy
        );

        for (int i = 0; i < 10; i++) {
            final int taskNumber = i + 1;
            executor.submit(() -> {
                System.out.println("Executing task " + taskNumber);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        executor.shutdown();
    }
}

使用 DiscardOldestPolicy:

import java.util.concurrent.*;

public class DiscardOldestPolicyExample {
    public static void main(String[] args) {
        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 10L, TimeUnit.SECONDS, queue,
            new ThreadPoolExecutor.DiscardOldestPolicy() // 使用 DiscardOldestPolicy
        );

        for (int i = 0; i < 10; i++) {
            final int taskNumber = i + 1;
            executor.submit(() -> {
                System.out.println("Executing task " + taskNumber);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        executor.shutdown();
    }
}

来点通俗易懂的

为了更好地理解这些拒绝策略,我们可以将其类比于生活中的场景:

  • AbortPolicy:就像餐厅已满员,不再接待新客人,并告知客人“已经客满,请去别处”。
  • CallerRunsPolicy:就像餐厅忙不过来时,老板自己上阵服务客人,保证所有客人都能被服务到。
  • DiscardPolicy:就像餐厅已满员,直接不理会新来的客人,不告知任何信息。
  • DiscardOldestPolicy:就像餐厅已满员,把最早来但还没点菜的客人请走,以便接待新来的客人。

自定义拒绝策略

除了内置的拒绝策略,开发者还可以根据实际需求自定义拒绝策略。例如,记录日志、发送通知等。

import java.util.concurrent.*;

public class CustomRejectionPolicyExample {
    public static void main(String[] args) {
        BlockingQueue<Runnable> queue = new ArrayBlockingQueue<>(2);
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 4, 10L, TimeUnit.SECONDS, queue,
            new RejectedExecutionHandler() {
                @Override
                public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                    System.err.println("Task " + r.toString() + " was rejected");
                    // 这里可以添加更多处理逻辑,比如记录日志、发送通知等
                }
            }
        );

        for (int i = 0; i < 10; i++) {
            final int taskNumber = i + 1;
            executor.submit(() -> {
                System.out.println("Executing task " + taskNumber);
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }

        executor.shutdown();
    }
}

结语

通过本文的介绍和代码示例,相信大家对 Java 线程池的拒绝策略有了更深入的理解。在实际开发中,选择合适的拒绝策略能有效提升系统的稳定性和用户体验。

责任编辑:赵宁宁 来源: 源话编程
相关推荐

2020-11-25 11:33:47

Java线程技术

2020-07-08 12:05:55

Java线程池策略

2020-02-18 14:25:51

Java线程池拒绝策略

2022-03-14 07:32:06

线程池拒绝策略自定义

2009-07-20 17:49:07

JSF请求处理

2021-06-17 09:32:39

重复请求并发请求Java

2024-11-13 16:37:00

Java线程池

2024-11-11 17:27:45

2023-08-15 15:33:29

线程池线程数

2024-01-08 09:09:40

线程池异常黑洞Futrue

2024-04-26 00:00:02

Java线程池策略

2023-12-20 10:04:45

线程池Java

2023-05-12 12:09:38

职责链模式客服

2024-09-30 08:54:10

2009-07-24 10:57:41

ASP.NET ISAIIS6

2011-04-13 14:57:11

ASP.NET请求处理

2011-04-13 15:50:49

.htmHTTP请求处理

2020-02-11 17:15:09

开发者抛弃 Executors

2012-06-26 10:03:06

海量数据处理

2009-09-24 17:11:53

Hibernate处理
点赞
收藏

51CTO技术栈公众号