欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

ThreadPoolExecutor是如何做到线程重用的

程序员文章站 2024-02-16 10:33:52
...



ThreadPoolExecutor是如何做到线程重用的





原创

2017年03月22日 11:29:18

        <ul class="article_tags clearfix csdn-tracking-statistics" data-mod="popu_377">
            <li class="tit">标签:</li>


  • 线程 /
  • 线程池 /






  • 前言:

    看关于ThreadPoolExecutor参数时,看到了keepaliveTime这个参数,这个参数的意思是:“当线程数大于CorePoolSize时,如果有没有等到新的Task,到了keepaliveTime时间后,就自动终止掉”。那么如果在这个时间之前,等到了新的Task,就可以重用这个线程。到底是怎么重用线程的呢?


    正文:

    原理如下:

    前提条件:假如coreSize=3,maxSize=10,当前存在线程数是5。
    (注意,存在的这5个线程,并不是你执行ExecuteService.execute/submit时的参数,而是为了执行execute/submit的参数所启动的“内部线程”。这个“内部线程”其实是通过ThreadPoolExecutor的ThreadFactory参数生成的线程,而“execute/submit的参数”是执行在这些“内部线程”里面的。)

    存在这5个“内部线程”,都访问同一个队列,从队列中去取任务执行(任务就是通过execute/submit提交的Runnable参数),当任务充足时,5个“内部线程”都持续执行。重点是没有任务时怎么办?
    没有任务时,这5个“内部线程”都会做下面判断:

    • 如果poolSize > coreSize,那就从队列里取任务,当过了keepaliveTime这么长时间还没有得到任务的话,当前这个“内部线程”就会结束(使用的是BlockingQueue.poll方法)。
    • 如果poolSize <= coreSize,那就以“阻塞”的方式,去从队列里取任务,当得到任务后,就继续执行。这样的话,这个线程就不会结束掉。

    如果没有任务可以继续执行了,最后只剩下coreSize那么多的“内部线程”留在线程池里,等待重用。

    相关代码如下:

    1,从ExecuteService.execute方法开始说,execute方法代码如下:

    int c = ctl.get();
    // 当“内部线程”数 < coreSize的话,就新建一个“内部线程”
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    // 能执行到这里,说明“内部线程”数 >= coreSize。这样的话,就把任务加入队列。
    // 这个队列,就是上面说的所有“内部线程”访问的那个队列。
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        reject(command);
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18


    2,下面再看一下addWorker方法,看看“内部线程”是如何建立的。

    // 上面一部分代码省略
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        // 创建一个Worker实例,把我们提交的任务(也就是firstTask)传入到Worker里。
        // 然后,取得Worker里的thread的属性,并在下面运行这个thread。
        // (其实这个thread属性,就是通过ThreadFactory生成的Thread,也就是所说的“内部线程”)
        // 接下来我们看看Worker类
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int rs = runStateOf(ctl.get());
    
                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43


    下面我们看看Worker类,其中和本次说明没有什么关系的代码省略。

    Worker(Runnable firstTask) {
        setState(-1); // inhibit interrupts until runWorker
        // 这里把提交的任务保存到Worker里,在后面执行的时候,从Worker里取得这个任务执行。
        this.firstTask = firstTask;
        // 下面就是生成“内部线程”的代码
        this.thread = getThreadFactory().newThread(this);
    }
    
    public void run() {
        // 这个方法,是“内部线程”实际上执行的方法。原理是这样的:
        // 上面构造函数在生成“内部线程”时,把Worker本身,当做参数传给了“内部线程”
        // “内部线程”在自己的run方法里,执行的是worker的run方法,而worker的run方法执行的是runWorker这个方法。
        // runWorker方法是ThreadPoolExectuor的方法,也就是说,线程池里的“内部线程”在调用run方法时,都是执行的这个方法
        //(所以这个方法要注意同步)
        runWorker(this);
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16


    3,最后我们看看runWorker方法。

    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); // allow interrupts
    boolean completedAbruptly = true;
    try {
        // task!=null是为了运行“内部线程”启动时,提交的给它的那个线程。
        // (task = getTask()) != null) 是当提交给他的任务执行完后,看看队列里还有没有任务可以执行。
        // 如果有的话,这个“内部线程”就可以重用了。
        // 而“内部线程都会做的判断”,就是getTask()方法里面做的,下面看一下getTask()方法
        while (task != null || (task = getTask()) != null) {
            w.lock();
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() &&
                  runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);
                Throwable thrown = null;
                try {
                    task.run();
                } catch (RuntimeException x) {// 省略异常处理
                } finally {
                    afterExecute(task, thrown);
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36


    getTask()方法原代码如下

    // 这个语句后半部分来判断“内部线程数 > coreSize”
    boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
    
    if ((wc > maximumPoolSize || (timed && timedOut))
        && (wc > 1 || workQueue.isEmpty())) {
        if (compareAndDecrementWorkerCount(c))
            return null;
        continue;
    }
    try {
        // 下面的判断,就是上面说的“内部线程都会做的判断”
        Runnable r = timed ?
            workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
            workQueue.take();
        if (r != null)
            return r;
        timedOut = true;
    } catch (InterruptedException retry) {
        timedOut = false;
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20


    最后

    如果要测试看看线程是不是被重用的话,可以通过自己实现ThreadFactory,加个打印语句,来查看newThread方法被调用的次数。下面的代码是参考Executors的代码,加了一条打印语句

    static class DefaultThreadFactory implements ThreadFactory {
        private static final AtomicInteger poolNumber = new AtomicInteger(1);
        private final ThreadGroup group;
        private final AtomicInteger threadNumber = new AtomicInteger(1);
        private final String namePrefix;
    
        DefaultThreadFactory() {
            SecurityManager s = System.getSecurityManager();
            group = (s != null) ? s.getThreadGroup() :
                    Thread.currentThread().getThreadGroup();
            namePrefix = "pool-" +
                    poolNumber.getAndIncrement() +
                    "-thread-";
        }
    
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                    namePrefix + threadNumber.getAndIncrement(),
                    0);
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            // 通过查看调用的次数,可以来判断new了几个内部线程
            System.out.println(namePrefix + threadNumber.get() + " is created " + "  hashcode:" + t.hashCode());
            return t;
        }
    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28


    其它

    下面的这篇文章是对线程池的一个说明,里面有一个简单的线程池的实现,和ThreadPoolExecutor有些类似,可以参考一下:

    Thread pools and work queues

     

     







    版权声明:本文为博主原创文章,未经博主允许随便转载。(你喜欢就好)


    • 本文已收录于以下专栏:







                      <form action="http://blog.csdn.net/hotdust/comment/submit?id=64905254" method="post" onsubmit="return subform(this);" id="commentform">
                          <textarea class="comment_content" name="comment_content" id="comment_content" placeholder="发表你的评论" style="height: 40px;"></textarea>
                          <div class="bot_bar clearfix" style="opacity: 0;">
                              <div id="ubbtools" class="add_code">
                                  <a href="#insertcode" code="code" target="_self"><i class="icon iconfont icon-daima"></i></a>
                              </div>
    
                              <input type="hidden" id="comment_replyId" name="comment_replyId">
                              <input type="hidden" id="comment_userId" name="comment_userId" value="">
                              <input type="hidden" id="commentId" name="commentId" value="">
                              <input type="submit" class="btn btn-redborder" value="发表评论">
                              <span id="tip_comment" class="tip">
                              <div style="display: none;" class="csdn-tracking-statistics" data-mod="popu_384"><a href="#" target="_blank" class="comment_area_btn">发表评论</a></div>
    
                              <div id="lang_list" code="code">
                                  <a href="#html" style="width:95px;" class="long_name" target="_self">HTML/XML</a>
                                  <a href="#objc" style="width:95px;" class="long_name" target="_self">objective-c</a>
                                  <a href="#delphi" style="width:58px;" class="zhong_name" target="_self">Delphi</a>
                                  <a href="#ruby" class="zhong_name" target="_self">Ruby</a>
                                  <a href="#php" target="_self">PHP</a>
                                  <a href="#csharp" class="duan_name" target="_self">C#</a>
                                  <a style=" border-right: none;" href="#cpp" class="duan_name" target="_self">C++</a>
                                  <a style=" border-bottom:none;width:95px;" href="#javascript" class="long_name" target="_self">JavaScript</a>
                                  <a style=" border-bottom:none;width:95px;" href="#vb" class="long_name" target="_self">Visual Basic</a>
                                  <a style=" border-bottom:none;" href="#python" class="zhong_name" target="_self">Python</a>
                                  <a style=" border-bottom:none;" href="#java" class="zhong_name" target="_self">Java</a>
                                  <a style="border-bottom:none;" href="#css" class="duan_name" target="_self">CSS</a>
                                  <a style="border-bottom:none;" href="#sql" class="duan_name" target="_self">SQL</a>
                                  <a style="border:none; " href="#plain" class="duan_name" target="_self">其它</a>
                                  <span class="arrb"></span>
                              </div>
                          </span></div>
                      </form>
                  </div>
                              </div>
          </div>
      </div>
      <div class="comment_li_outbox"></div>
    
      <div class="more_comment" style="display: none;">
          <div id="comment_bar" class="trackgin-ad" data-mod="popu_385"></div>
      </div>
    
      <h3 class="recommend_tit">相关文章推荐</h3>
      <div class="recommend_list clearfix" id="rasss">
                                                                                      <dl class="clearfix csdn-tracking-statistics" data-mod="popu_387" data-poputype="feed" data-feed-show="false" data-dsm="post">
                      <dd>
                          <h2><a href="http://blog.csdn.net/zwk626542417/article/details/47084925" target="_blank" strategy="BlogCommendFromBaidu_0">跟我学Java多线程——ThreadPoolExecutor(线程池)</a></h2>
                          <div class="summary">
                              关于线程池ThreadPoolExecutor我们本篇文章讲解了线程池是什么以及使用线程池的好处,并且通过一个简单的demo来让大家对线程池有一个整体认识,最后介绍了线程池ThreadPoolExec...                          </div>
                          <ul>
                              <li class="avatar_img"><a href="http://blog.csdn.net/zwk626542417" target="_blank" strategy="BlogCommendFromBaidu_0"><img src="http://avatar.csdn.net/1/D/6/3_zwk626542417.jpg" alt="zwk626542417" title="zwk626542417"></a></li>
                              <li class="user_name"><a href="http://blog.csdn.net/zwk626542417" target="_blank">zwk626542417</a></li>
                              <li class="time">2015年07月27日 14:57</li>
                              <li class="visited_num"><i class="icon iconfont icon-read"></i><span>4019</span></li>
                          </ul>
                      </dd>
                  </dl>
                                                                                                    <dl class="clearfix csdn-tracking-statistics" data-mod="popu_387" data-poputype="feed" data-feed-show="false" data-dsm="post">
                      <dd>
                          <h2><a href="http://blog.csdn.net/zhangzeyuaaa/article/details/49247535" target="_blank" strategy="BlogCommendFromBaidu_1">线程重用——线程池的基本原理</a></h2>
                          <div class="summary">
                              为简单起见,线程池中只有一个线程:package com.xs.concurrent;
    

    import java.util.concurrent.BlockingQueue;
    import java….






    (function() {
    var s = "_" + Math.random().toString(36).slice(2);
    document.write('
    ');
    (window.slotbydup=window.slotbydup || []).push({
    id: '4765209',
    container: s,
    size: '808,120',
    display: 'inlay-fix'
    });
    })();


    ThreadPoolExecutor是如何做到线程重用的ThreadPoolExecutor是如何做到线程重用的



    【免费技术直播】数据科学家,从入门到精进

    数据科学家究竟是一群怎样的人?来自北美数据科学职场前线,为你带来作为数据科学家的第一手经验..







    Java 多线程编程之九:使用 Executors 和 ThreadPoolExecutor 实现的 Java 线程池的例子



    线程池用来管理工作线程的数量,它持有一个等待被执行的线程的队列。
            java.util.concurrent.Executors 提供了 java.util.concurrent.Exe…


    • ThreadPoolExecutor是如何做到线程重用的
    • defonds
    • 2013年08月02日 12:39
    • 22766





    Java中的线程池——ThreadPoolExecutor的使用



    开发过程中,合理地使用线程池可以带来3个好处:
    降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
    提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
    提高线…






    java并发包学习系列:线程复用之线程池



    频繁使用new Thread来创建线程的方式并不太好。因为每次new Thread新建和销毁对象性能较差,线程缺乏统一管理。好在Java提供了线程池,它能够有效的管理、调度线程,避免过多的资源消耗。优…


    • ThreadPoolExecutor是如何做到线程重用的
    • a910626
    • 2016年07月13日 19:45
    • 2530





    (function() {
    var s = "_" + Math.random().toString(36).slice(2);
    document.write('
    ');
    (window.slotbydup=window.slotbydup || []).push({
    id: '4983339',
    container: s,
    size: '808,120',
    display: 'inlay-fix'
    });
    })();


    ThreadPoolExecutor是如何做到线程重用的ThreadPoolExecutor是如何做到线程重用的



    人人都能看懂的 AI 入门课

    本课程将讲述人工智能的现状、应用场景和入门方法,并通过运用 TensorFlow,使得受众能清晰了解人工智能的运作方式。







    Java 线程池ThreadPoolExecutor(基于jdk1.8)(二)



    上一篇分析了ThreadPoolExecutor的execute方法的具体执行过程,这一篇主要分析当中的几个重要的函数。…






    Java多线程:ThreadPoolExecutor详解



    ThreadPoolExecutor是JDK并发包提供的一个线程池服务,基于ThreadPoolExecutor可以很容易将一个Runnable接口的任务放入线程池中。
    ThreadPoolExecu…






    Java史上最大误解,你真的以为LinkedList比ArrayList增删快?



    从我们学习java的List时候就被灌输这样的一个概念:
    ArrayList随机访问快;
    LinkedList增删快;然而事实真的是这样吗?请看以下代码:(任何人都能复制这份代码进行尝试)pack…






    80秒验证13亿个身份证号码,包含省市县验证



    我写了一个验证身份证号码的程序,它是以一定内存空间(大概100M)换取cpu消耗,然后它的运算量就降低了,前十四位的验证就相当于转换类型再查表一样,所以它的验证号码速度比一般的方式快。如果还不明白就说…






    ThreadPoolExecutor 是如何做到线程重用的



    前言:

    看关于ThreadPoolExecutor参数时,看到了keepaliveTime这个参数,这个参数的意思是:“当线程数大于CorePoolSize时,如果有没有等到新的Task,到了ke…







    线程测试参数ThreadPoolExecutor()



    1       JDK 自带线程池

    线程池类为 java.util.concurrent.ThreadPoolExecutor,常用构造方法为:

    ThreadPoolExecutor(in…







    ThreadPoolExecutor线程池及线程扩展策略



    一、概述 
    1、ThreadPoolExecutor作为java.util.concurrent包对外提供基础实现,以内部线程池的形式对外提供管理任务执行,线程调度,线程池管理等等服务; 
    2、E…






    Java 线程池学习 Reference: 《创建Java线程池》[1],《Java线程:新特征-线程池》[2], 《Java线程池学习》[3],《线程池ThreadPoolExecutor使用简介



    Java 线程池学习

    Reference: 《创建Java线程池》[1],《Java线程:新特征-线程池》[2], 《Java线程池学习》[3],《线程池ThreadPoolExecutor使用…







    ThreadPoolExecutor,worker和线程工厂之间理解



    ThreadPoolExecutor中一个线程就是一个Worker对象,它与一个线程绑定,当Worker执行完毕就是线程执行完毕,这个在后面详细讨论线程池中线程的运行方式。而Worker带了锁,根据我…






    Java多线程之~~~使用ThreadPoolExecutor来创建线程



    以前我们创建线程的时候都是主动的new一个Thread,然后调用他们的start方法,但是如果线程非常多,任务也非
    常多的时候,这样写就会显得非常麻烦,当然可能效率也不是很高,Java给我们提供了叫线…






    JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用简介,线程邮件发送实例



    在多线程大师Doug Lea的贡献下,在JDK1.5中加入了许多对并发特性的支持,例如:线程池。
    一、简介
    线程池类为 java.util.concurrent.ThreadPoolExecuto…






    线程学习三:线程池ThreadPoolExecutor 与 Executors



    /**
         * Creates a new {@code ThreadPoolExecutor} with the given initial
         * parameters.
         *…


    • ThreadPoolExecutor是如何做到线程重用的
    • xubo_ob
    • 2016年10月18日 21:29
    • 411





    ThreadPoolExecutor中运行线程名称的修改



    项目中使用到了ThreadPoolExecutor,这个是挺好的东西,线程池的实现。但有一点不太爽的是,用Jprofiler调试由它创建的线程的时候,看到的都是pool-1-thread-1\2\3…






    java线程API学习 线程池ThreadPoolExecutor



    转自:http://blog.csdn.net/ABBuggy/archive/2011/06/16/6548843.aspx

    线程池ThreadPoolExecutor继承自ExecutorSer…







    Java多线程 之 ThreadPoolExecutor(九)



    最近在工作中遇到了ThreadPoolExecutor的使用,而且是由于它的配置不当导致了线上问题。下面对其进行简单介绍。
    先看看ThreadPoolExecutor常用的构造方法: public…





    相关标签: thread