【多线程】线程池提交的任务,被拒绝策略处理时,能不能获取到当时子任务的实例对象呢? 队列的处理,排队效果模拟

为了看一下拒绝策略的拒绝效果是什么样的
,我通过匿名内部类的方式自定义了一个拒绝策略,并实现了其中的拒绝方法rejectedExecution(),而对于其中固定的参数,是否可以通过转换获取到真实的Runnable实现类的实例呢
? 答案是不能的。

接下来我们一起验证一下,同时也看下具体,线程池提交一些任务,哪个任务会进队列,哪个会被核心线程直接执行,哪个又会被拒绝
说明:因为每个子任务需要执行的业务逻辑没有,我通过sleep()来模拟实际业务花费的时间;

下面的具体的代码:

package com.atguigu.gulimall.providerconsumer.test;

import java.util.concurrent.*;

/**
 *
 * 验证拒绝策略的拒绝效果
 * @author: jd
 * @create: 2024-07-17
 */
public class RejectThreadPoolDemo {
    String diuqinme = "";

    public static void main(String[] args) throws InterruptedException {

        ExecutorService es = new ThreadPoolExecutor(1, 2, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<Runnable>(1), Executors.defaultThreadFactory(), new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                if(r instanceof MyTask){
                    System.out.println("r 是 MyTask的实例");
                    MyTask r1 = (MyTask) r;
                    System.out.println("队列已满,任务名称 :"+r1.toString() + "被丢弃;");

                }else{
                    System.out.println("(r instanceof MyTask) = " + (r instanceof MyTask));
                    System.out.println("队列已满,任务名称被丢弃 (这里还得验证怎么获取子任务的name);");
                }
            }
        });
        for (int i = 0; i < 4; i++) {
            MyTask task = new MyTask("name-" + i);
            System.out.println("准备提交任务,任务名称 = " + task.toString());
            es.submit(task);
            Thread.sleep(1000); //提交完一个任务之后,先睡眠一秒,再提交下面一个; 为了看见效果清晰,所以都是过一秒提交再进行提交
        }
        es.shutdown();
    }

    public static class MyTask implements Runnable {
        String name;

        MyTask(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return name;
        }

        @Override
        public void run() {
            System.out.println(System.currentTimeMillis() + "正在执行的任务name :" + this.name);

            try {
                //每个任务睡眠10s模拟业务逻辑花费的时间
                Thread.sleep(10000);
                System.out.println("正在执行的任务name :" + this.name+"执行结束");
            } catch (InterruptedException e) {
                System.out.println(e);
            }
        }
    }

}

解释一下,我线程池的创建参数: 核心线程 1个, 最大线程数 2个,没任务之后,存活时间0s(非核心线程直接关闭),消息队列我用的是有界队列,队列容量为1,拒绝策略 是自定义的一个拒绝策略,并重写了他的拒绝动作(为了让他在拒绝后不直接跑异常,而是提示一下,这样其他的业务可以继续执行。
特别注意哦~ 当然这是在自己玩的过程中,实际的生产中 对于被拒绝的任务或者数据都需要记录下来,或者队列采用无界队列、有固定参数的有界队列。线程数设置的稍微大一些。否则任务的直接丢失会造成一些比较严重的问题,可以将日志信息记录的更详细一点,来分析系统的负载和任务丢失的情况。
参数设置可见我的另一篇博文:【多线程】线程池的最优参数设置,如何考虑?

上面代码的实验效果
效果1:
我提交了四个任务,而且每个任务的执行时间较长(我这里设置了每个任务执行10s),所以这就导致只要是我提交到线程池中的任务,用完线程池中的线程都不会立刻释放,因为他还没执行完,所以还要占用这,
解释一下现象:让我们更清晰一些
首先,准备提交任务名称name-0 然后直接被线程池中的核心线程执行了,所以可见到第二行,name-0正在执行。
然后,又每隔1s提交了,任务name-1 name-2 ,然后为什么任务name-1没有直接被启动的非核心线程来执行呢 ?我猜测是非核心线程最初没启动着 ,是关闭着的,所以当name-1被提交的时候,只有一个线程,而且这个线程被name-0占用着,所以name-1被扔到队列中了,队列容量=1,所以此时队列被占满了。
然后,提交了任务name-2,此时非核心线程启动了,正好name-2赶上了,所以name-2成功提交,打印出了开始执行。
然后我又间隔1 s ,提交了name-3 ,这个是第四个想要提交的任务,因为我是每个1s提交一个任务的,但是每个任务的运行时间是10s。所以第4秒提交完name-3之后,所,有的线程(1个核心【在执行name-0】+1个非核心【在执行name-2】),队列中有一个name-1在排队,所以此时name-3就没地方去了,就被丢弃了,所以是这个任务触发了拒绝策略,但我本想着在拒绝策略中的rejectedExecution()来打印出来拒绝了哪个任务,
但是我尝试了很多办法都没有获取到提交的任务的实现类实例【我查了资料,如下】

在 Java 的 RejectedExecutionHandler 接口中,rejectedExecution 方法的参数 Runnable r 之所以不是你具体提交的 Runnable 实现类的直接类型,主要是因为 Java 的类型系统和接口的设计方式。
当你向 ThreadPoolExecutor 或其他实现了 ExecutorService 接口的线程池提交一个任务时,这个任务是通过
Runnable 或 Callable 接口的实例来提交的。这两个接口是 Java 并发框架中的核心接口,用于定义可以被线程执行的任务。
当你定义一个具体的 Runnable 实现类时,你实际上是在实现一个通用的任务执行接口。这个接口规定了任务应该如何被执行(通过 run
方法),但它并不关心任务的具体实现细节或类型。因此,当你将这个实现类的实例提交给线程池时,线程池只看到它是一个 Runnable(或 Callable),而不知道它背后的具体实现类是什么。

当线程池因为某些原因(如线程池关闭、线程池已满且工作队列也满等)无法执行这个任务时,它会调用你提供的
RejectedExecutionHandler 的 rejectedExecution 方法,并将被拒绝的任务(作为 Runnable
类型的引用)和线程池本身作为参数传递给这个方法。

在这个上下文中,Runnable r 是你提交的任务的引用,但它被当作 Runnable 接口的实例来处理,而不是你具体的实现类。这是因为
RejectedExecutionHandler 接口是设计为通用的,可以处理任何实现了 Runnable接口的任务,而不需要知道这些任务的具体类型。

文献中说可以通过进行类型检查和转换,r instanceof MyTask ,但是我实验的效果:拒绝策略中得到的不是提交任务(Runnable的实现类)的实例。只是一个Runnable对象。所以我还没找到什么方式可以获取到具体任务的name

**接着说效果:**再过几秒,被提交的任务到10s后就结束了,所以name-0执行结束,然后在核心线程空出来了,所以在队列中的name-1被提交,然后紧接着name-2执行结束,然后最后是name-1,这样就执行完毕了。
在这里插入图片描述

效果2:
其他参数不变,我调整有界队列的长度 从1改成了2,,因为我只是提交了4个任务,所以总共下来的都够用的,都被执行成功了。

在这里插入图片描述
OK,到这里就完事了,现象演示结束啦,而且疑问也验证出来了。但是也留下了一个疑问,拒绝策略中是否能获取到提交的任务实现类的name,我会继续验证,大佬们有清楚的,积极分享评论哦,谢谢。

码字不易,还望大家多多点赞,支持一下博主,Thanks♪(・ω・)ノ。

  • 7
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值