Java 线程池拒绝策略

Java 线程池 ThreadPoolExecutor 拒绝策略

线程池参数

  1. corePoolSize:线程池中的常驻核心线程数。
  2. maximumPoolSize:线程池允许的最大线程数。
  3. keepAliveTime:当线程数大于核心线程数时,多余的空闲线程等待新任务的最大时间。
  4. unit:keepAliveTime的时间单位。
  5. workQueue:任务队列,用于在任务太多时存储多余的任务。
  6. threadFactory:创建线程的工厂,可以设置线程的名字和优先级等。
  7. handler:拒绝策略,当任务太多且队列也满时,如何处理新任务。

1. AbortPolicy(中止策略 - 默认策略)

说明: 不执行新的任务并抛出RejectedExecutionException异常

应用场景:适用于对系统的稳定性要求较高,不希望丢失任务,但希望能快速发现任务提交失败的情况。

/**
     * A handler for rejected tasks that throws a
     * {@link RejectedExecutionException}.
     *
     * This is the default handler for {@link ThreadPoolExecutor} and
     * {@link ScheduledThreadPoolExecutor}.
     */
    public static class AbortPolicy implements RejectedExecutionHandler {
        /**
         * Creates an {@code AbortPolicy}.
         */
        public AbortPolicy() { }

        /**
         * Always throws RejectedExecutionException.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         * @throws RejectedExecutionException always
         */
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            throw new RejectedExecutionException("Task " + r.toString() +
                                                 " rejected from " +
                                                 e.toString());
        }
    }
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadPoolAbortPolicy {

  private static final AtomicInteger TICKETS = new AtomicInteger(30);

  public static void main(String[] args) {
    try (
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            2,  // 核心线程数
            5,  // 最大线程数
            0L, // 空闲时间
            TimeUnit.SECONDS, // 空闲时间单位
            new LinkedBlockingQueue<>(10),  // 阻塞队列
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()  // 默认拒绝策略
        )
    ) {
      for (int i = 0; i < 30; i++) {
        threadPoolExecutor.execute(() -> {
          if (TICKETS.get() > 0) {
            System.out.println("===> 售票员: " + Thread.currentThread().getName() + ", 售出票号为: " + TICKETS.getAndDecrement());
          }
        });
      }
    }

    try {
      Thread.sleep(1000);
    }catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      e.printStackTrace();
    }

  }

}

/*
	总共可以售票15张, 因为设置最大线程数为5, 且队列最大的数量为10, 剩下的因为队列已经满了, 所以会被拒绝抛出 RejectedExecutionException.
    
    ===> 售票员: pool-1-thread-4, 售出票号为: 27
    ===> 售票员: pool-1-thread-5, 售出票号为: 26
    ===> 售票员: pool-1-thread-3, 售出票号为: 28
    ===> 售票员: pool-1-thread-1, 售出票号为: 30
    ===> 售票员: pool-1-thread-2, 售出票号为: 29
    ===> 售票员: pool-1-thread-4, 售出票号为: 25
    ===> 售票员: pool-1-thread-5, 售出票号为: 24
    ===> 售票员: pool-1-thread-3, 售出票号为: 23
    ===> 售票员: pool-1-thread-1, 售出票号为: 22
    ===> 售票员: pool-1-thread-2, 售出票号为: 21
    ===> 售票员: pool-1-thread-4, 售出票号为: 20
    ===> 售票员: pool-1-thread-5, 售出票号为: 19
    ===> 售票员: pool-1-thread-3, 售出票号为: 18
    ===> 售票员: pool-1-thread-1, 售出票号为: 17
    ===> 售票员: pool-1-thread-2, 售出票号为: 16
    Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.apollo.pool.ThreadPoolAbortPolicy$$Lambda$14/0x00000008010029f8@9f70c54 rejected from java.util.concurrent.ThreadPoolExecutor@1e67b872[Running, pool size = 5, active threads = 5, queued tasks = 10, completed tasks = 0]
        at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2081)
        at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:841)
        at 	java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1376)
        at com.apollo.pool.ThreadPoolAbortPolicy.main(ThreadPoolAbortPolicy.java:26)
*/

2. CallerRunsPolicy(调用者运行策略)

说明: 当任务被拒绝添加时,如果调用者所在的线程不是线程池中的线程,那么任务将由该调用者所在的线程直接执行。

**应用场景:**适用于系统不希望丢弃任务,并且对任务的执行延迟要求不高的情况。当线程池已经饱和时,新任务会在提交任务的线程中执行,可能会导致提交任务的线程阻塞。

 /**
     * A handler for rejected tasks that runs the rejected task
     * directly in the calling thread of the {@code execute} method,
     * unless the executor has been shut down, in which case the task
     * is discarded.
     */
public static class CallerRunsPolicy implements RejectedExecutionHandler {
    /**
         * Creates a {@code CallerRunsPolicy}.
         */
    public CallerRunsPolicy() { }

    /**
         * Executes task r in the caller's thread, unless the executor
         * has been shut down, in which case the task is discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadPoolCallerRunsPolicy {

  private static final AtomicInteger TICKETS = new AtomicInteger(30);

  public static void main(String[] args) {
    try (
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 5, 0L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(10),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.CallerRunsPolicy()
        )
    ) {
      for (int i = 0; i < 30; i++) {
        executor.execute(() ->
            System.out.println("===> 售票员: " + Thread.currentThread().getName() + ", 售出票号为: " + TICKETS.getAndDecrement())
        );
      }
    }

    try {
      Thread.sleep(6000);
    }catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      e.printStackTrace();
    }
    
  }

}


/*
	===> 售票员: pool-1-thread-1, 售出票号为: 30
    ===> 售票员: main, 售出票号为: 29
    ===> 售票员: pool-1-thread-4, 售出票号为: 26
    ===> 售票员: pool-1-thread-3, 售出票号为: 27
    ===> 售票员: pool-1-thread-5, 售出票号为: 25
    ===> 售票员: pool-1-thread-2, 售出票号为: 28
    ===> 售票员: pool-1-thread-1, 售出票号为: 24
    ===> 售票员: main, 售出票号为: 23
    ===> 售票员: pool-1-thread-4, 售出票号为: 22
    ===> 售票员: pool-1-thread-3, 售出票号为: 21
    ===> 售票员: pool-1-thread-5, 售出票号为: 20
    ===> 售票员: pool-1-thread-2, 售出票号为: 19
    ===> 售票员: pool-1-thread-1, 售出票号为: 18
    ===> 售票员: main, 售出票号为: 17
    ===> 售票员: pool-1-thread-4, 售出票号为: 16
    ===> 售票员: pool-1-thread-3, 售出票号为: 15
    ===> 售票员: pool-1-thread-5, 售出票号为: 14
    ===> 售票员: pool-1-thread-5, 售出票号为: 8
    ===> 售票员: pool-1-thread-2, 售出票号为: 13
    ===> 售票员: pool-1-thread-1, 售出票号为: 12
    ===> 售票员: main, 售出票号为: 11
    ===> 售票员: pool-1-thread-4, 售出票号为: 10
    ===> 售票员: pool-1-thread-3, 售出票号为: 9
    ===> 售票员: pool-1-thread-5, 售出票号为: 7
    ===> 售票员: pool-1-thread-2, 售出票号为: 6
    ===> 售票员: pool-1-thread-1, 售出票号为: 5
    ===> 售票员: pool-1-thread-4, 售出票号为: 4
    ===> 售票员: pool-1-thread-3, 售出票号为: 3
    ===> 售票员: pool-1-thread-5, 售出票号为: 2
    ===> 售票员: pool-1-thread-2, 售出票号为: 1
*/

3. DiscardPolicy(丢弃策略)

说明:默默丢弃无法处理的任务,不抛出异常。

应用场景:适用于可以容忍任务丢失的情况,例如不重要的日志处理任务。

/**
     * A handler for rejected tasks that silently discards the
     * rejected task.
     */
public static class DiscardPolicy implements RejectedExecutionHandler {
    /**
         * Creates a {@code DiscardPolicy}.
         */
    public DiscardPolicy() { }

    /**
         * Does nothing, which has the effect of discarding task r.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Function: 线程池拒绝策略 - DiscardPolicy 丢弃任务策略
 */
public class ThreadPoolDiscardPolicy {

  private static final AtomicInteger TICKETS = new AtomicInteger(30);

  public static void main(String[] args) {

    try (
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 5, 0L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(10),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.DiscardPolicy()
        )
    ) {
      for (int i = 0; i < 30; i++) {
        executor.execute(() ->
            System.out.println("===> 售票员: " + Thread.currentThread().getName() + ", 售出票号为: " + TICKETS.getAndDecrement())
        );
      }
    }

    try {
      Thread.sleep(1000);
    }catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      e.printStackTrace();
    }

  }

}

/*
	===> 售票员: pool-1-thread-5, 售出票号为: 26
    ===> 售票员: pool-1-thread-4, 售出票号为: 27
    ===> 售票员: pool-1-thread-5, 售出票号为: 25
    ===> 售票员: pool-1-thread-4, 售出票号为: 24
    ===> 售票员: pool-1-thread-3, 售出票号为: 28
    ===> 售票员: pool-1-thread-5, 售出票号为: 23
    ===> 售票员: pool-1-thread-1, 售出票号为: 30
    ===> 售票员: pool-1-thread-2, 售出票号为: 29
    ===> 售票员: pool-1-thread-4, 售出票号为: 22
    ===> 售票员: pool-1-thread-3, 售出票号为: 21
    ===> 售票员: pool-1-thread-5, 售出票号为: 20
    ===> 售票员: pool-1-thread-1, 售出票号为: 19
    ===> 售票员: pool-1-thread-2, 售出票号为: 18
    ===> 售票员: pool-1-thread-4, 售出票号为: 17
    ===> 售票员: pool-1-thread-3, 售出票号为: 16
*/

4. DiscardOldestPolicy(丢弃最旧策略)

说明:丢弃任务队列中最旧的未处理任务,然后将新任务加入队列重新尝试执行。

应用场景:适用于任务的重要性相对较低,且希望尽量处理新提交的任务的情况。

/**
     * A handler for rejected tasks that discards the oldest unhandled
     * request and then retries {@code execute}, unless the executor
     * is shut down, in which case the task is discarded. This policy is
     * rarely useful in cases where other threads may be waiting for
     * tasks to terminate, or failures must be recorded. Instead consider
     * using a handler of the form:
     * <pre> {@code
     * new RejectedExecutionHandler() {
     *   public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
     *     Runnable dropped = e.getQueue().poll();
     *     if (dropped instanceof Future<?>) {
     *       ((Future<?>)dropped).cancel(false);
     *       // also consider logging the failure
     *     }
     *     e.execute(r);  // retry
     * }}}</pre>
     */
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    /**
         * Creates a {@code DiscardOldestPolicy} for the given executor.
         */
    public DiscardOldestPolicy() { }

    /**
         * Obtains and ignores the next task that the executor
         * would otherwise execute, if one is immediately available,
         * and then retries execution of task r, unless the executor
         * is shut down, in which case task r is instead discarded.
         *
         * @param r the runnable task requested to be executed
         * @param e the executor attempting to execute this task
         */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Function: 线程池拒绝策略 - DiscardOldestPolicy 丢弃最老的任务策略
 */
public class ThreadPoolDiscardOldestPolicy {

  private static final AtomicInteger TICKETS = new AtomicInteger(30);

  public static void main(String[] args) {

    try (
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 5, 0L, TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(10),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.DiscardOldestPolicy()
        )
    ) {
      for (int i = 0; i < 30; i++) {
        executor.execute(() ->
            System.out.println("===> 售票员: " + Thread.currentThread().getName() + ", 售出票号为: " + TICKETS.getAndDecrement())
        );
      }
    }

    try {
      Thread.sleep(1000);
    }catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      e.printStackTrace();
    }

  }

}


/*
	===> 售票员: pool-1-thread-2, 售出票号为: 29
    ===> 售票员: pool-1-thread-5, 售出票号为: 26
    ===> 售票员: pool-1-thread-1, 售出票号为: 30
    ===> 售票员: pool-1-thread-4, 售出票号为: 27
    ===> 售票员: pool-1-thread-3, 售出票号为: 28
    ===> 售票员: pool-1-thread-2, 售出票号为: 25
    ===> 售票员: pool-1-thread-5, 售出票号为: 24
    ===> 售票员: pool-1-thread-1, 售出票号为: 23
    ===> 售票员: pool-1-thread-4, 售出票号为: 22
    ===> 售票员: pool-1-thread-3, 售出票号为: 21
    ===> 售票员: pool-1-thread-2, 售出票号为: 20
    ===> 售票员: pool-1-thread-5, 售出票号为: 19
    ===> 售票员: pool-1-thread-1, 售出票号为: 18
    ===> 售票员: pool-1-thread-4, 售出票号为: 17
    ===> 售票员: pool-1-thread-3, 售出票号为: 16
*/

5. 自定义拒绝策略

说明: 需要实现接口 RejectedExecutionHandler, 重写 void rejectedExecution(Runnable r, ThreadPoolExecutor executor) 方法.

package com.apollo.pool;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

public class ThreadPoolSelfDefinePolicy {

  private static final AtomicInteger TICKETS = new AtomicInteger(30);

  public static void main(String[] args) {

    try (
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
            2, 5, 0L, TimeUnit.SECONDS,
           new LinkedBlockingQueue<>(10),
           Executors.defaultThreadFactory(),
           new SelfDefinePolicy()
        )
    ) {
      for (int i = 0; i < 30; i++) {
        executor.execute(() -> {
          if (TICKETS.get() > 0) {
            System.out.println("===> 售票员: " + Thread.currentThread().getName() + ", 售出票号为: " + TICKETS.getAndDecrement());
          }
        });
      }
    }

    try {
      Thread.sleep(1000);
    }catch (InterruptedException e) {
      Thread.currentThread().interrupt();
      e.printStackTrace();
    }


  }

  static class SelfDefinePolicy implements RejectedExecutionHandler {
    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
      // 记录拒绝的任务
      System.out.println("===> 自定义拒绝策略: Task " + r.toString() + " rejected.");
      // 抛出自定义的异常
      throw new RejectedExecutionException("Task " + r + " rejected.");
    }
  }

}

/*
	===> 自定义拒绝策略: Task com.apollo.pool.ThreadPoolSelfDefinePolicy$$Lambda$14/0x0000000801002c10@9f70c54 rejected.
    ===> 售票员: pool-1-thread-2, 售出票号为: 29
    ===> 售票员: pool-1-thread-1, 售出票号为: 30
    ===> 售票员: pool-1-thread-3, 售出票号为: 28
    ===> 售票员: pool-1-thread-5, 售出票号为: 27
    ===> 售票员: pool-1-thread-4, 售出票号为: 26
    ===> 售票员: pool-1-thread-2, 售出票号为: 25
    ===> 售票员: pool-1-thread-1, 售出票号为: 24
    ===> 售票员: pool-1-thread-3, 售出票号为: 23
    ===> 售票员: pool-1-thread-5, 售出票号为: 22
    ===> 售票员: pool-1-thread-4, 售出票号为: 21
    ===> 售票员: pool-1-thread-2, 售出票号为: 20
    ===> 售票员: pool-1-thread-1, 售出票号为: 19
    ===> 售票员: pool-1-thread-3, 售出票号为: 18
    ===> 售票员: pool-1-thread-5, 售出票号为: 17
    ===> 售票员: pool-1-thread-4, 售出票号为: 16
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task com.apollo.pool.ThreadPoolSelfDefinePolicy$$Lambda$14/0x0000000801002c10@9f70c54 rejected.
	at com.apollo.pool.ThreadPoolSelfDefinePolicy$SelfDefinePolicy.rejectedExecution(ThreadPoolSelfDefinePolicy.java:56)
	at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:841)
	at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1376)
	at com.apollo.pool.ThreadPoolSelfDefinePolicy.main(ThreadPoolSelfDefinePolicy.java:30)
	
*/
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值