Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ThreadPoolExecutor 饱和策略部分的错误 #802

Closed
zhuanglee opened this issue Jun 1, 2020 · 3 comments
Closed

ThreadPoolExecutor 饱和策略部分的错误 #802

zhuanglee opened this issue Jun 1, 2020 · 3 comments

Comments

@zhuanglee
Copy link

@zhuanglee zhuanglee commented Jun 1, 2020

有一个疑问

ThreadPoolExecutor.CallerRunsPolicy 当最大池被填满时,此策略为我们提供可伸缩队列。(这个直接查看 ThreadPoolExecutor 的构造函数源码就可以看出,比较简单的原因,这里就不贴代码了)

我看了 ThreadPoolExecutor 的构造函数源码并没发现当最大池被填满时, ThreadPoolExecutor.CallerRunsPolicy 可以提供可伸缩队列。

然后搜索相关问题发现以下链接中ThreadPoolExecutor 饱和策略部分完全一样,错误部分已加粗

如果当前同时运行的线程数量达到最大线程数量并且队列也已经被放满了任时,ThreadPoolTaskExecutor 定义一些策略:
ThreadPoolExecutor.AbortPolicy:抛出 RejectedExecutionException来拒绝新任务的处理。
ThreadPoolExecutor.CallerRunsPolicy:调用执行自己的线程运行任务。您不会任务请求。但是这种策略会降低对于新任务提交速度,影响程序的整体性能。另外,这个策略喜欢增加队列容量。如果您的应用程序可以承受此延迟并且你不能任务丢弃任何一个任务请求的话,你可以选择这个策略。
ThreadPoolExecutor.DiscardPolicy: 不处理新任务,直接丢弃掉。
ThreadPoolExecutor.DiscardOldestPolicy: 此策略将丢弃最早的未处理的任务请求。

https://snailclimb.gitee.io/javaguide/#/docs/java/Multithread/JavaConcurrencyAdvancedCommonInterviewQuestions

http://www.mianshigee.com/question/10150vgo

https://blog.csdn.net/qq_23145857/article/details/105366265

https://blog.csdn.net/agonie201218/article/details/106187740

https://www.cnblogs.com/1994jinnan/p/12331591.html

https://www.lagou.com/lgeduarticle/72192.html

https://www.cnblogs.com/ustc-anmin/p/11753277.html

@LiWenGu
Copy link
Contributor

@LiWenGu LiWenGu commented Jun 3, 2020

JavaGuide 仓库里面初期部分博文是参考 CSDN 的。。。属于历史原因~

我们先看官方注释:
Java 里面关于 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

我的理解:当线程队列满时,直接在调用 execute 方法的调用线程中运行被拒绝的任务。

至于你贴的博文所说的「喜欢增加队列容量」,这句话有问题,因为它本身没有任何增加队列的源码,不信你看源码:

public static class CallerRunsPolicy implements RejectedExecutionHandler {
   
        public CallerRunsPolicy() { }

        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            if (!e.isShutdown()) {
                r.run();
            }
        }
    }

源码很简单,就是当触发拒绝策略时,直接在调用线程上下文运行被拒绝的任务
最后为了加深理解,你可以运行下面的代码,仔细 debug 琢磨:

static ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(1), new ThreadPoolExecutor.CallerRunsPolicy());

    public static void main(String[] args) throws InterruptedException {
        threadPoolExecutor.submit((Runnable) () -> {
            while (true) {
                try {
                    System.out.println("第一个线程上下文" + Thread.currentThread().getName());
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException ignored) {
                }
            }
        });
        TimeUnit.SECONDS.sleep(2);
        System.out.println("第一个任务执行中,当前线程池里正在跑的线程数/最大线程数/队列中的线程数:" + threadPoolExecutor.getActiveCount() + "/" + threadPoolExecutor.getMaximumPoolSize() + "/" + threadPoolExecutor.getQueue().size());
        TimeUnit.SECONDS.sleep(2);
        threadPoolExecutor.submit((Runnable) () -> {
            while (true) {
                try {
                    // 第一个问题:为什么这行不会打印?
                    System.out.println("第二个线程上下文" + Thread.currentThread().getName());
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        System.out.println("第二个任务执行中:当前线程池里正在跑的线程数/最大线程数/队列中的线程数:" + threadPoolExecutor.getActiveCount() + "/" + threadPoolExecutor.getMaximumPoolSize() + "/" + threadPoolExecutor.getQueue().size());
        TimeUnit.SECONDS.sleep(2);
        threadPoolExecutor.submit((Runnable) () -> {
            while (true) {
                try {
                    System.out.println("第三个线程上下文" + Thread.currentThread().getName());
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException ignored) {
                }
            }
        });
        // 第二个问题:为什么这行不会打印?
        System.out.println("第三个任务执行中:当前线程池里正在跑的线程数/最大线程数/队列中的线程数:" + threadPoolExecutor.getActiveCount() + "/" + threadPoolExecutor.getMaximumPoolSize() + "/" + threadPoolExecutor.getQueue().size());
    }
@LiWenGu LiWenGu mentioned this issue Jun 3, 2020
@zhuanglee
Copy link
Author

@zhuanglee zhuanglee commented Jun 3, 2020

  1. 第二个runnable在队列中等待,所以不会打印;
  2. 最后一句sout未打印,是因为主线程被第三个runnable中的while阻塞;

我明白 CallerRunsPolicy 的作用,但是文中所说的 CallerRunsPolicy 可以提供可伸缩队列,让我觉得CallerRunsPolicy 改变了队列的容量。

@LiWenGu
Copy link
Contributor

@LiWenGu LiWenGu commented Jun 3, 2020

文章毕竟是作者根据个人理解和能力写的,有错误笔误误导在所难免。代码这东西还是得习惯自己看源码和注释。如果有其它困惑记得提 issue,一起探讨

@Snailclimb Snailclimb closed this Jun 6, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
3 participants