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

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简单线程池 线程池中文文档

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

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

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

    java线程池完整代码

    标题 "Java 线程池完整代码" 表明了这篇文章的主题是关于 Java 线程池的完整实现代码,涵盖了线程池的基本概念、配置文件的解析、线程池的创建和管理等方面。 描述解析 描述 "Java 线程池完整源码" 说明了这篇文章...

    一个通用的Java线程池类

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

    java线程池使用后到底要关闭吗

    java线程池使用后到底要关闭吗 java线程池是一种高效的并发编程技术,可以帮助开发者更好地管理线程资源,提高系统的性能和可靠性。然而,在使用java线程池时,一个常见的问题是:使用完线程池后到底要不要关闭?...

    Java实现通用线程池

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

    java线程池封装j

    Java线程池封装是Java并发编程中重要的一环,合理的线程池配置和封装能显著提升程序的性能和稳定性。理解线程池的工作原理,根据业务需求选择合适的参数,以及正确处理拒绝策略,都是实现高效并发处理的关键。在实际...

    java多线程,对多线程,线程池进行封装,方便使用

    在Java中,我们可以通过实现Runnable接口或继承Thread类来创建线程。然而,直接使用线程可能存在一些问题,如资源管理不当、线程安全问题以及线程生命周期的控制等。为了解决这些问题,Java提供了线程池(ThreadPool...

    Java线程池使用说明

    1. Executor:它是Java中线程池的顶级接口,定义了执行线程的抽象方法,但它本身并不直接提供线程池功能。 2. ExecutorService:是真正的线程池接口,提供了一系列方法来管理线程的生命周期以及任务的执行。 3. ...

    Java中多线程的使用线程池.docx

    Java中的线程池是多线程编程中一种高效、可管理的执行机制。它通过预先创建并维护一组线程,避免了频繁地创建和销毁线程带来的开销,从而提高了程序的性能和响应速度。线程池的核心概念包括以下几个方面: 1. **...

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

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

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

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

    线程池java

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

    自定义实现Java线程池

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

    java实现通用线程池的代码

    本文旨在使用Java语言编写一个通用的线程池。当需要使用线程池处理事务时,只需按照指定规范封装好事务处理对象,然后用已有的线程池对象去自动选择空 闲线程自动调用事务处理对象即可。并实现线程池的动态修改...

    java 线程池

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

    Java中的线程与线程池.pptx

    Java中的线程与线程池是程序设计中的关键概念,特别是在多任务并行处理和优化系统性能时。本文档深入探讨了这两个主题,旨在帮助开发者更好地理解和应用它们。 线程是为了充分利用CPU资源而引入的执行单元。在多...

    Java 线程池.pptx

    讲述了java线程池的优点,参数,6种线程池的使用场景,线程池用到的handler,线程任务的提交方式等等。

    JAVA线程池例子

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

    Java 线程池.docx

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

Global site tag (gtag.js) - Google Analytics