`
san_yun
  • 浏览: 2655089 次
  • 来自: 杭州
文章分类
社区版块
存档分类
最新评论

Java中容易踩到的“坑”系列之线程池篇

 
阅读更多

原文:http://hellojava.info/?p=13

 

在有了java.util.concurrent包后,通常都会采用Executors或ThreadPoolExecutor来创建线程池,但这两个类都挺容易用错,如果不仔细阅读它的API或源码的话,很容易就踩进坑里。

通过new ThreadPoolExecutor可创建相应配置的线程池,但这个接口挺容易被误用,也许会导致行为和你想要的不太一样,之前在微博上我出了下面这道题。

有以下两个线程池:
ThreadPoolExecutor aPool = new ThreadPoolExecutor(10,20,5,TimeUnit.MINUTES,new SynchronousQueue<Runnable>());
ThreadPoolExecutor bPool = new ThreadPoolExecutor(10,20,5,TimeUnit.MINUTES,new ArrayBlockingQueue<Runnable>(10));
目前已有10个请求正在处理中,请问当第11个请求进来时aPool和bPool分别会如何处理?

从评论上来看,答错的人还是有一些的,这道题的正确答案是:
当第11个请求进来时,aPool会启动线程来立刻处理,bPool会放入BlockingQueue,等待前面的线程处理完才会被处理。

原因是ThreadPoolExecutor的实现机制为当目前运行的线程数 >= corePoolSize(也就是上面的10)时,ThreadPoolExecutor会将请求放进Queue中(例如上面的aPool是 SynchronousQueue),如放失败且目前的线程数 < maxPoolSize(也就是上面的20),那么就直接启动新线程来处理,如目前运行的线程数等于maxPoolSize,则执行对应的策略,上面的 aPool和bPool使用的都是默认的拒绝并跑出异常的策略;如放成功则进行再次的检查(例如线程池是否被shutdown、运行的线程数是否小于 corePoolSize),然后返回。

SynchronousQueue是一个很特殊的Queue,当往这个Queue放东西时,必须有另外一个线程在从这个Queue里拿,如没有,则 直接失败,在上面的场景中,当第11个请求进来时,往这个Queue中放就将失败,而这个时候运行的线程数又小于maxPoolSize,因此将启动新线 程进行处理。
而bPool采用的是ArrayBlockingQueue,put将成功,可见在bPool的情况下,想要运行的线程数增加到11个,必须是10个线程 已经在处理中,并且ArrayBlockingQueue已经排队了10个请求,那么这个时候如果再有第21个请求进来,才会启动第11个线程进行处理, 刚用这个API时,很容易会认为只有当线程数已经达到了maxPoolSize后才会往Queue里放。

Executors是ThreadPoolExecutor的包装,基于它可以更简单的去创建线程池,但在使用它创建线程池也要特别的注意,下面就说说这个类中常用的几个方法创建的线程池的一些特征:

1. newCachedThreadPool
这种方式创建出来的线程池corePoolSize为0,maxPoolSize为Integer.MAX_VALUE,BlockingQueue为 SynchronousQueue,因此这个线程池的行为为如线程均在运行中,新任务需要执行,则直接启动线程,直到运行的线程数达到 Integer.MAX_VALUE。
这种线程池在某些情况下可能会创建非常大量的线程,鉴于这个原因,不推荐使用。
2. newFixedThreadPool
这种方式创建出来的线程池corePoolSize和maxPoolSize均为指定的大小,BlockingQueue为 LinkedBlockingQueue,因此这个线程池的行为为如同时处理的请求小于指定的大小,那么就创建新的线程来处理,如达到了指定的大小,则放 入Queue中。
这种线程池在某些情况下可能会出现Queue中堆积了很多的任务,导致内存被耗光的现象,因此也不推荐使用,如有类似需求,建议使用限定大小的ArrayBlockingQueue等。

最后无论是用哪种方式创建的线程池,最好都增加下名字,可通过实现ThreadPoolFactory接口来做到(例如这个), 否则的话当线程池的线程处于等待任务处理时,如这个时候jstack,会看到一堆类似pool-x-thread-y的线程名字,堆栈中也都是 java.util之类的,如这个时候问题出在这个线程池创建的线程过多时,那排查起来就比较麻烦些(那就只能挂起btrace来跟踪)。

分享到:
评论

相关推荐

    Java中容易踩到的“坑”系列之线程池篇 – hellojavacases - 毕玄 - 林昊1

    同步队列基于数组的有界队列无容量的同步队列有容量的有界队列拒绝执行策略任务入队列策略、线程创建策略– parking to wait for &lt;0x000000

    Java简单线程池 线程池中文文档

    简单的线程池程序+中文文档 包结构: com.tangkai.threadpool --SimpleThread.java 工作线程 --TestThreadPool.java 程序入口 --ThreadPoolManager.java 线程池管理类

    Java8并行流中自定义线程池操作示例

    Java8并行流中自定义线程池操作示例 Java8并行流中自定义线程池操作示例主要介绍了Java8并行流中自定义线程池操作,结合实例形式分析了并行流的相关概念、定义及自定义线程池的相关操作技巧。 1. 概览 Java8引入了...

    Java中Executors类中几种创建各类型线程池

    Java中Executors类中几种创建各类型线程池方法及简单实例

    Java实现通用线程池

    在 Java 中,线程池可以使用ThreadPoolExecutor类来实现,ThreadPoolExecutor类可以实现线程池的创建、管理和销毁。线程池的核心思想是预先创建一些空闲线程,等到需要用多线程去处理事务的时候去唤醒某些空闲线程...

    一个通用的Java线程池类

    目标:Java中多线程技术是一个难点,但是也是一个核心技术。因为Java本身就是一个多线程语言。本人目前在给46班讲授Swing的网络编程--使用Swing来模拟真实的QQ实时聊天软件。因为涉及到Socket编程,所以一定会使用多...

    java线程池实例详细讲解

    在Java中,`ExecutorService`接口是线程池的主要入口,它是`java.util.concurrent`包的一部分,提供了创建、管理和控制线程池的功能。 线程池的核心概念包括以下几点: 1. **工作队列(Work Queue)**:线程池内部...

    Java编程中线程池的最大风险规避

    ### Java编程中线程池的最大风险规避 #### 死锁 在Java编程中,线程池带来的一个显著风险就是死锁。死锁是指多个线程因互相等待对方持有的资源而不释放自身资源,导致无限期等待的情况。对于一般的多线程程序而言...

    Java实现的线程池、消息队列功能

    在Java中,`java.util.concurrent`包下的`ExecutorService`、`ThreadPoolExecutor`和`Executors`类提供了线程池的实现。 消息队列(Message Queue)则是一种异步通信机制,它允许应用程序将消息发送到队列,然后由...

    Java版线程池实现

    在Java中,线程池的实现主要依赖于`java.util.concurrent`包中的`ExecutorService`接口以及它的实现类,如`ThreadPoolExecutor`。不过,上述代码展示的是一个自定义的线程池实现,它可能没有使用Java标准库中的`...

    线程池java

    在Java中,`ThreadPoolExecutor`是线程池的核心实现类之一,它提供了丰富的配置选项来满足不同的应用场景需求。该类继承自`AbstractExecutorService`,实现了线程池的主要功能。 **构造方法详解**: ```java ...

    自定义实现Java线程池

    在Java中,线程池的核心接口是`Executor`,它是所有线程池实现的基础。该接口定义了一个`execute()`方法,用于提交任务进行执行。 ```java public interface Executor { void execute(Runnable command); } ``` #...

    java线程池知识.ppt

    java线程池知识、

    Java/Android线程池演示Demo

    在Android和Java应用开发中,线程池是一种重要的并发编程工具,它可以帮助我们高效地管理后台任务,提高系统的响应速度和资源利用率。本Demo主要展示了如何在Java或Android环境中使用线程池,以下是对相关知识点的...

    java 线程池

    Java中创建线程池的主要工具类是`java.util.concurrent.ExecutorService`,它提供了一种标准的方式创建不同类型的线程池。常用的线程池实现包括: 1. **FixedThreadPool**:创建固定大小的线程池,可以复用指定数量...

    JAVA线程池例子

    线程池在Java中主要通过`java.util.concurrent`包中的`ExecutorService`接口及其实现类来实现。本示例将深入探讨线程池的基本概念、工作原理以及如何在实际开发中应用。 线程池的核心组件包括:工作队列(Work ...

    Java 线程池.docx

    在Java中,通过使用线程池,我们可以预先创建一定数量的线程,这些线程在空闲时可以被复用,从而避免了每次创建新线程时的系统开销。此外,线程池还可以控制并发量,防止过多线程导致系统资源耗尽,从而提高系统的...

    JAVA集中常用的线程池比较.pdf

    在Java中,线程池的实现主要依赖于`java.util.concurrent`包中的`ExecutorService`接口和其相关的类,如`ThreadPoolExecutor`。 线程池的工作原理是:当一个任务提交到线程池时,如果池中有空闲线程,那么这个任务...

    java线程池实例

    在Java中,`java.util.concurrent`包提供了`ExecutorService`接口和它的实现类,如`ThreadPoolExecutor`,来支持线程池的创建和管理。本实例将深入探讨如何在Java中使用线程池,以及其背后的原理和最佳实践。 首先...

    java socket(线程池)

    是一个java在进行socket编程时,关于线程池的介绍,有代码和例子

Global site tag (gtag.js) - Google Analytics