`
zhengjunwei2007_163.com
  • 浏览: 130373 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

jdk6线程池ThreadPoolExecutor 总结

    博客分类:
  • JAVA
阅读更多

这里主要总结池子的管理线程、处理任务的流程,以采用LinkedBlockingQueue  corePoolSize=maxPoolSzie=5  KeepAliveTime=0L 为例解说:

 

 ThreadPoolExecutor .execute(Runnable),向池中存放任务;随着execute 到corePoolsize任务时,池初始化启动了corePoolsize个线程,池子维护这5个线程;这5个线程监控Queue等待take任务。

当corePoolSzie线程就绪后,新进任务直接queue.offer();

       

         /**
         * Main run loop
         */
        public void run() {
            try {
                Runnable task = firstTask;
                firstTask = null;
                while (task != null || (task = getTask()) != null) {
                    runTask(task);
                    task = null;
                }
            } finally {
                workerDone(this);
            }
        }

池中线程,一直处于dotask ...queue.take()...dotask的循环之中。而queue.take()和poll()的区别就是直到取到有效element才返回;

 

 引用别人解说:

所有 BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:

  • 如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。(什么意思?如果当前运行的线程小于corePoolSize,则任务根本不会存放,添加到queue中,而是直接抄家伙(thread)开始运行
  • 如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列而不添加新的线程
  • 如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。
  • 排队有三种通用策略:

    1. 直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
    2. 无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有 corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize 的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web 页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。
    3. 有界队列。当使用有限的 maximumPoolSizes 时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。  

     

    例子一:使用直接提交策略,也即SynchronousQueue。

     

    首先SynchronousQueue是无界的,也就是说他存数任务的能力是没有限制的,但是由于该Queue本身的特性在某次添加元素后必须等待其他线程取走后才能继续添加。在这里不是核心线程便是新创建的线程,但是我们试想一样下,下面的场景。

    例子二:使用无界队列策略,即LinkedBlockingQueue

    这个就拿newFixedThreadPool来说,根据前文提到的规则:
     写道
    如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。
     那么当任务继续增加,会发生什么呢?
     写道

     

    如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。

     OK,此时任务变加入队列之中了,那什么时候才会添加新线程呢?

     

     写道
    如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

    这里就很有意思了,可能会出现无法加入队列吗?不像SynchronousQueue那样有其自身的特点,对于无界队列来说,总是可以加入的(资源耗尽,当然另当别论)。换句说,永远也不会触发产生新的线程!corePoolSize大小的线程数会一直运行,忙完当前的,就从队列中拿任务开始运行。所以要防止任务疯长,比如任务运行的实行比较长,而添加任务的速度远远超过处理任务的时间,而且还不断增加,如果任务内存大一些,不一会儿就爆了,呵呵。

     

    总结:

    1. ThreadPoolExecutor的使用还是很有技巧的。
    2. 使用无界queue可能会耗尽系统资源。
    3. 使用有界queue可能不能很好的满足性能,需要调节线程数和queue大小
    4. 线程数自然也有开销,所以需要根据不同应用进行调节。
    通常来说对于静态任务可以归为:
    1. 数量大,但是执行时间很短
    2. 数量小,但是执行时间较长
    3. 数量又大执行时间又长
    4. 除了以上特点外,任务间还有些内在关系
    keepAliveTime

    jdk中的解释是:当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。

    有点拗口,其实这个不难理解,在使用了“池”的应用中,大多都有类似的参数需要配置。比如数据库连接池,DBCP中的maxIdle,minIdle参数。
    总结:

    keepAliveTime和maximumPoolSize及BlockingQueue的类型均有关系。如果BlockingQueue是无界的,那么永远不会触发maximumPoolSize,自然keepAliveTime也就没有了意义。

    反之,如果核心数较小,有界BlockingQueue数值又较小,同时keepAliveTime又设的很小,如果任务频繁,那么系统就会频繁的申请回收线程。
    RejectedExecutionHandler
    在ThreadPoolExecutor中已经默认包含了4中策略:
    CallerRunsPolicy: 线程调用运行该任务的 execute 本身。
    AbortPolicy:处理程序遭到拒绝将抛出运行时 RejectedExecutionException
    DiscardPolicy:不能执行的任务将被删除
    DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)
    分享到:
    评论

    相关推荐

      JDK自带线程池分析

      ThreadPoolExecutor 是 JDK 自带线程池的核心类,提供了线程池的管理和执行功能。该类提供了多种方法来创建和管理线程池,例如固定大小的线程池、单线程的线程池和计划线程池等。 创建线程池 创建线程池有多种方法...

      java线程池ThreadPoolExecutor类使用详解.docx

      而线程池不允许使用Executors去创建,而要通过ThreadPoolExecutor方式,这一方面是由于jdk中Executor框架虽然提供了如newFixedThreadPool()、newSingleThreadExecutor()、newCachedThreadPool()等创建线程池的方法,...

      jdk自带线程池实例详解

      jdk自带的线程池主要是通过ThreadPoolExecutor类来实现的。ThreadPoolExecutor类是Executor框架中最核心的类,它提供了一个灵活的线程池管理机制,允许开发者根据需要创建不同类型的线程池。 二、jdk自带线程池的...

      JDK1.5线程池源码及详细注释

      JDK 1.5引入了java.util.concurrent包,其中包含了线程池的实现,使得并发编程更加便捷和高效。线程池的核心在于它的设计策略,包括核心线程数、最大线程数、线程存活时间、工作队列以及拒绝策略。 线程池的主要类...

      JDK1[1].5中的线程池(ThreadPoolExecutor)使用简介

      JDK1[1].5中的线程池(ThreadPoolExecutor)使用简介

      JDK1.5中的线程池

      JDK1.5中的线程池(ThreadPoolExecutor)使用简介

      JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用

      "JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用" JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用是Java多线程编程中的一种重要概念。随着多线程编程的普及,线程池的使用变得...

      JDK线程池和Spring线程池的使用实例解析

      JDK线程池可以通过ThreadPoolExecutor类来创建,ThreadPoolExecutor类提供了一个ExecutorService接口的实现。 在使用JDK线程池时,我们可以通过ThreadPoolExecutor类来创建一个线程池,例如: ```java private ...

      自定义实现Java线程池1-模拟jdk线程池执行流程1

      本篇将探讨如何模拟Java的JDK线程池执行流程,以理解其设计原理。核心知识点包括线程池的执行策略、接口设计以及异常处理。 首先,Java中的线程池设计始于JDK 5.0,主要通过`java.util.concurrent`包中的`Executor`...

      JDK1[1].5中的线程池(ThreadPoolExecutor)使用简介.

      corePoolSize: 线程池维护线程的最少数量 maximumPoolSize:线程池维护线程的最大数量 keepAliveTime: 线程池维护线程所允许的空闲时间 unit: 线程池维护线程所允许的空闲时间的单位 workQueue: 线程池所使用的...

      JDK之ThreadPoolExecutor源码分析1

      《JDK之ThreadPoolExecutor源码分析1》 在Java编程中,线程池是一种高效的管理线程的方式,它通过复用已存在的线程来避免频繁创建和销毁线程带来的开销。ThreadPoolExecutor作为Java中的线程池实现,其内部机制相当...

      JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用简介.doc

      JDK1.5中的线程池(java.util.concurrent.ThreadPoolExecutor)使用简介

      jdk1.5 线程并发与线程池的使用

      在Java编程语言中,线程并发和线程池是多任务执行的核心概念,尤其是在JDK 1.5及以后的版本中得到了显著增强。线程并发允许程序同时执行多个任务,提高了程序的效率和响应性。线程池是管理线程资源的有效方式,通过...

      笔记-6、线程池1

      我们可以扩展`java.util.concurrent.ThreadPoolExecutor`类,它提供了更复杂的线程池管理功能,包括工作队列、核心线程数、最大线程数等参数。 3. JDK 中的线程池和工作机制 JDK内置了多种线程池实现,如`...

      java线程池概念.txt

      3:对线程池的基本使用及其部分源码的分析(注意:这里的源码分析是基于jdk1.6;) a:线程池的状态 volatile int runState; static final int RUNNING = 0; 运行状态 static final int SHUTDOWN = 1; 关闭状态;...

      【2018最新最详细】并发多线程教程

      21.线程池ThreadPoolExecutor实现原理 22.线程池之ScheduledThreadPoolExecutor 23.FutureTask基本操作总结 24.Java中atomic包中的原子操作类总结 25.大白话说java并发工具类-CountDownLatch,CyclicBarrier 26....

      Java线程池使用说明

      在JDK 1.5版本之前,Java对线程池的支持非常有限,而在JDK 1.5之后,加入了java.util.concurrent包,其中包含了一系列关于线程池的接口和类,极大地丰富了线程池的应用场景和管理方式。 线程池的主要作用是限制系统...

      Java线程池文档

      但在JDK 1.5及以上版本,推荐使用`ThreadPoolExecutor`,它是线程池的核心实现,提供了更丰富的功能和配置选项。 `ThreadPool`类的构造函数接收一个`poolSize`参数,表示线程池中工作线程的数量,然后创建相应数量...

      java线程池的使用方式

      在早期的JDK版本(如JDK 1.4及之前)中,线程池的功能相对简单,使用起来不够灵活。然而,自JDK 1.5开始,随着`java.util.concurrent`包的引入,Java线程池的功能得到了极大的增强,为开发者提供了更加高效且易于...

      java socket线程池

      从JDK 1.5开始,Java并发API得到了增强,提供了更为强大的并发工具和库,其中就包括线程池的实现。线程池的主要优势在于可以重用线程,减少线程创建和销毁带来的开销,同时还可以有效控制并发数和管理资源。 在给出...

    Global site tag (gtag.js) - Google Analytics