线程池拒绝策略

今天看了线程池的拒绝策略:

我们看下ThreadPoolExecutor的API:看它的构造方法:

最后一个参数就是今天的主角:拒绝策略

首先点开这个类

我们发现他是一个接口,点开它的实现类,我们发现这些实现类就是我们所说的各种任务拒绝策略

我们一个一个分析

首先我们发现构造方法里有不需要拒绝策略的构造方法。那么一定就有一个默认的构造方法我们从这个讲起:

这就是我们的默认拒绝策略:它的拒绝方式是直接抛出异常。简单粗暴

试验:

我们自定义一个线程池:核心线程池大小为1,最大线程池大小为1,设置保留线程的时间为0,使用SynchronousQueue队列实现(这个队列是缓冲线程池中的队列,我们只需要知道在这里它拒绝接受线程就好了)

源码:

思路:当我们接受一个线程让他沉睡数秒,此时另一个线程进来,我们看看会发生什么

代码如下:

import java.util.concurrent.*;

class Thread1 extends Thread {
    public void run() {
        try {
            Thread.sleep(1000);//线程1沉睡数秒,保证线程2进入线程池时,线程1还在运行中
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程" + Thread.currentThread().getName() + "输出:Threada");
    }
}


class Thread2 extends Thread {
    public void run() {
        System.out.println("线程" + Thread.currentThread().getName() + "输出:Threadb");
    }

}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();

        ExecutorService executorService = new ThreadPoolExecutor(1, 1, 0,
                TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
        executorService.execute(thread1);//执行线程1

        executorService.execute(thread2);//线程1沉睡时线程2来了

        executorService.shutdown();//关闭

    }
}

 

执行结果:

抛出异常首先,然后1线程结束了沉睡完成输出

总结:这个拒绝策略就是当线程池满员,我们就直接抛出异常

 

2.再看一个

这个拒绝策略也很简单,它的意思就是,假如现在使用到了拒绝策略,那么我就将这些被拒绝的线程用当前调用它们的线程执行这些任务

如:线程2被拒绝,是main线程调用这个线程开启的任务,因此我们直接去使用main的线程执行这些被拒绝的任务,如果执行线程池关闭了,那么就放弃这个任务

源码:

试验:

和上面的参数相同,只是改变了执行策略:代码如下:

import java.util.concurrent.*;

class Thread1 extends Thread {
    public void run() {
        try {
            Thread.sleep(1000);//线程1沉睡数秒,保证线程2进入线程池时,线程1还在运行中
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程" + Thread.currentThread().getName() + "输出:Threada");
    }
}


class Thread2 extends Thread {
    public void run() {
        System.out.println("线程" + Thread.currentThread().getName() + "输出:Threadb");
    }

}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        ExecutorService executorService= new ThreadPoolExecutor(1,1,0,
        TimeUnit.SECONDS,new SynchronousQueue<Runnable>(),new 
        ThreadPoolExecutor.CallerRunsPolicy());
        executorService.execute(thread1);//执行线程1
        executorService.execute(thread2);//线程1沉睡时线程2来了
        executorService.shutdown();//关闭

    }
}

 执行结果如下:

我们发现时用过了main线程执行了b任务,和上面说的一样

 

再看第三个:

 当我们达到了执行拒绝策略的条件时,我们将任务队列中的最早的任务,也就是队列头移除,这时候再一次执行这个execute操作,这时没有其他因素影响的话我们就进了队列,等待执行了

源码:

测试如下:

我们设置三个线程,线程1先开启进入睡眠,然后线程2进入队列,此时再让线程3进来,我们的的预期结果是,把线程2踢出队列,线程3进入队列,线程1沉睡完后就执行线程3,线程2此时就被抛弃了:

import java.util.concurrent.*;

class Thread1 extends Thread {
    public void run() {
        try {
            Thread.sleep(1000);//线程1沉睡数秒,保证线程2进入线程池时,线程1还在运行中
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程" + Thread.currentThread().getName() + "输出:Threada");
    }
}


class Thread2 extends Thread {
    public void run() {
        System.out.println("线程" + Thread.currentThread().getName() + "输出:Threadb");
    }
}

class Thread3 extends Thread {
    public void run() {
        System.out.println("线程" + Thread.currentThread().getName() + "输出:Threadc");
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        Thread3 thread3 = new Thread3();
        ExecutorService executorService= new ThreadPoolExecutor(1,1,0,
        TimeUnit.SECONDS,  new LinkedBlockingQueue<Runnable>(1),new ThreadPoolExecutor.DiscardOldestPolicy());
        executorService.execute(thread1);//执行线程1
        executorService.execute(thread2);//线程1沉睡时线程2来了,进入了工作队列等待
        executorService.execute(thread3);//线程1沉睡时线程3来了,此时线程2位于队列中,队列满了,我们执行拒绝策略
        executorService.shutdown();//关闭

    }
}

执行结果:

先停顿了一下,然后输出上面的结果,我们发现并没有线程2执行

 

第四种:

这个很简单其实就是直接丢弃并且不抛出异常:

源码如下:

测试:

我们还是先让a进入后睡眠,此时b进来到了队列,c也进来,但是c该被拒绝通过拒绝策略,所以直接丢弃

代码如下:

 

import java.util.concurrent.*;

class Thread1 extends Thread {
    public void run() {
        try {
            Thread.sleep(1000);//线程1沉睡数秒,保证线程2进入线程池时,线程1还在运行中
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("线程" + Thread.currentThread().getName() + "输出:Threada");
    }
}


class Thread2 extends Thread {
    public void run() {
        System.out.println("线程" + Thread.currentThread().getName() + "输出:Threadb");
    }
}

class Thread3 extends Thread {
    public void run() {
        System.out.println("线程" + Thread.currentThread().getName() + "输出:Threadc");
    }
}

public class Main {
    public static void main(String[] args) throws InterruptedException {
        Thread1 thread1 = new Thread1();
        Thread2 thread2 = new Thread2();
        Thread3 thread3 = new Thread3();
        ExecutorService executorService= new ThreadPoolExecutor(1,1,0,
        TimeUnit.SECONDS,  new LinkedBlockingQueue<Runnable>(1),new ThreadPoolExecutor.DiscardPolicy());
        executorService.execute(thread1);//执行线程1
        executorService.execute(thread2);//线程1沉睡时线程2来了,进入了工作队列等待
        executorService.execute(thread3);//线程1沉睡时线程3来了,此时线程2位于队列中,队列满了,我们执行拒绝策略,放弃线程3
        executorService.shutdown();//关闭

    }
}

执行结果如下:

只输出了a和b

 

pis:我们看了拒绝策略的源码,发现其实,当用到拒绝策略时我们都会调用拒绝类里的rejectedExecution方法方法

### Java线程池拒绝策略概述 Java线程池提供了多种内置的拒绝策略来处理任务提交超出线程池容量的情况。这些策略可以通过`ThreadPoolExecutor`类进行设置,具体实现依赖于`RejectedExecutionHandler`接口。 #### 内置拒绝策略详解 1. **AbortPolicy** 默认的拒绝策略,在无法执行新任务时会抛出`RejectedExecutionException`异常[^4]。这种方式适用于希望程序立即失败并报告错误的场景。 2. **CallerRunsPolicy** 当任务被拒绝时,该策略会让提交任务的线程(即调用者线程)执行这个任务。这可能会降低系统的整体吞吐量,但在某些情况下可以缓解压力。 3. **DiscardPolicy** 静默丢弃被拒绝的任务而不做任何通知或记录。这种策略适合那些允许丢失部分任务的应用场景。 4. **DiscardOldestPolicy** 试图丢弃最旧的一个请求以便为当前任务腾出空间。此策略可能会影响较早提交的任务完成时间。 #### 自定义拒绝策略 除了上述四种标准策略外,还可以通过继承`RejectedExecutionHandler`接口来自定义拒绝逻辑。例如,`AbortPolicyWithReport`扩展了默认的`AbortPolicy`,可以在拒绝任务的同时提供额外的日志或其他操作支持[^2]。 以下是创建自定义拒绝策略的一个简单例子: ```java public class CustomRejectPolicy implements RejectedExecutionHandler { @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { System.err.println("Task " + r.toString() + " rejected from " + executor.toString()); // 可在此处添加其他处理逻辑,比如保存到队列或者日志记录 } } ``` #### 设置拒绝策略的方法 要在线程池中应用特定的拒绝策略,需在初始化`ThreadPoolExecutor`实例时指定它。下面是一个完整的示例代码片段展示如何配置带有不同拒绝策略线程池: ```java import java.util.concurrent.*; public class ThreadPoolExample { public static void main(String[] args) { int corePoolSize = 2; int maximumPoolSize = 4; long keepAliveTime = 10L; TimeUnit unit = TimeUnit.SECONDS; BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(2); // 创建具有CallerRunsPolicy拒绝策略线程池 ThreadPoolExecutor threadPool = new ThreadPoolExecutor( corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, new ThreadPoolExecutor.CallerRunsPolicy() ); // 提交超过线程池承载能力的任务数测试效果 for (int i = 0; i < 10; i++) { final int taskNumber = i; threadPool.submit(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } System.out.println("Executing Task " + taskNumber); }); } threadPool.shutdown(); } } ``` #### 最佳实践建议 为了更高效地利用线程池资源并减少因拒绝策略引发的问题,应遵循以下几点最佳实践: - 合理设定核心线程数(`corePoolSize`)和最大线程数(`maximumPoolSize`)以匹配实际负载需求[^3]。 - 调整阻塞队列大小(`workQueue`)以平衡内存占用与响应速度之间的关系。 - 定期监控线程池运行状况,包括活动线程计数、已完成任务总数等指标,并据此动态调整参数。 - 根据业务特点选择合适的拒绝策略;如果需要更高的灵活性,则考虑开发定制化解决方案。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值