Java线程池Executors

简介:

一 简述

   线程池,作为一个管理一组同构工作线程的资源。接受提交的任务,利用线程池中的线程进行工作的处理。 在另一篇《Java多线程设计模式(4)线程池模式》利用非Executors描述了线程池基本构建过程,对于线程池基本机制进行了说明。由于Java类库中有Executor来专门用于线程池的管理的类,所以可以用Executor任务执行框架来实现线程池的构建。

   Executor核心的思想就是将请求处理任务的提交线程和任务的实际执行解耦开来。利用execute来传递一个具体执行的Runnable任务类,或者利用submit来传递一个Runnable任务类或Callable获取任务返回值的任务。

   对于每次通过execute方法提交的任务执行顺序如下:

   1、会判断当前池线程以及核心数目的大小,当池中当前的线程数小于核心线程数时,会创建新的线程。具体创建新线程流程如:获取内置锁,将任务添加到内部的BlockingQueue任务队列中,再利用工厂方法产生一个执行该任务的线程,这个线程是非守护及优先级是NORM的线程。

   2、当线程数大于等于核心线程数,且任务队列未满时,将任务放入任务队列。

   3、当线程数大于等于核心线程数,且任务队列已满,采用以下处理方式

      若线程数小于最大线程数,创建线程

      若线程数等于最大线程数,抛出异常,拒绝任务,进行饱和处理策略。

   Executor任务执行框架类图如下:

   093837743.png

二 ThreadPoolExecutor线程池

    一般线程池可以直接利用构造器来实例化一个ThreadPoolExecutor,也可以利用Executors工具类来创建一个线程池。也可以通过继承扩展ThreadPoolExecutor来自定义一些ThreadPoolExecutor子类。

在利用Executors静态生成ThreadPoolExecutor的时候,都会在内部实例化一个ThreadPoolExecutorThreadPoolExecutor内部都是用BlockingQueue队列来保存提交的任务Runnable

    一个ThreadPoolExecutor需要考虑三个方面

    一个是线程池的大小,二个是任务队列的大小,三个是饱和策略

        常见的用法就是利用Executors的静态工厂来创建。

        标准的ThreadPoolExecutor构造方法如下:

1
2
3
4
5
6
7
public  ThreadPoolExecutor( int  corePoolSize,
         int  maximumPoolSize,
         long  keepAliveTime,
         TimeUnit unit,
         BlockingQueue<Runnable> workQueue,
         ThreadFactory threadFactory,
         RejectedExecutionHandler handler)


ThreadPoolExecutor的几个参数说明:

corePoolSize

核心线程数,核心线程会一直存活,即使没有任务需要处理。当线程数小于核心线程数时,即使现有的线程空闲,线程池也会优先创建新线程来处理任务,而不是直接交给现有的线程处理。

核心线程在allowCoreThreadTimeout被设置为true时会超时退出,默认情况下不会退出。

maxPoolSize

当线程数大于或等于核心线程,且任务队列已满时,线程池会创建新的线程,直到线程数量达到maxPoolSize。如果线程数已等于maxPoolSize,且任务队列已满,则已超出线程池的处理能力,线程池会拒绝处理任务而抛出异常。

keepAliveTime

当线程空闲时间达到keepAliveTime,该线程会退出,直到线程数量等于corePoolSize。如果allowCoreThreadTimeout设置为true,则所有线程均会退出直到线程数量为0

allowCoreThreadTimeout

是否允许核心线程空闲退出,默认值为false

queueCapacity

任务队列容量。从maxPoolSize的描述上可以看出,任务队列的容量会影响到线程的变化,因此任务队列的长度也需要恰当的设置。


Executors静态工厂创建线程池解释

Executors.newCachedThreadPool();

这种会创建一个目标数目或核心数目为0,最大数目为Integer.MAX_VALUE超时设置1分钟,并且利用SynchronousQueue来存储任务的ThreadPoolExecutorSynchronousQueue是一种同步移交队列,任务的生产者线程直接将任务移交给任务的消费者线程即工作线程。对于很大的线程池,可以利用SynchronousQueue避免任务的排队。要想将一个元素放入该队列,必须有另一个线程正在等待接收这个元素。当一个新的任务提交后,如果没有线程在等待,且线程池的当前值大小小于最大值,一般ThreadPoolExecutor都会创建一个新的线程。否则就会调用饱和策略来处理。SynchronousQueue这种队列一般用于当线程池无界或者很大的时候采用,目的就是更快的提交任务,充分利用线程池中的工作线程。

Executors.newFixedThreadPool(n);

这种会创建核心数目以及最大数目都是指定初始值的线程池,线程不会超时也就是不会被回收,并且利用无界的(最大值是Integer.MAX_VALUE)LinkedBlockingQueue来存储任务的ThreadPoolExecutor

Executors.newSingleThreadExecutor();

这种会创建核心数目以及最大数目都是1的线程池,线程不会超时也就是不会被回收,并且利用无界的(最大值是Integer.MAX_VALUE)LinkedBlockingQueue来存储任务的ThreadPoolExecutor

饱和策略解释:
当有界队列被填满后,就需要考虑如何对于再次发送的请求处理。

ThreadPoolExecutor中可以通过setRejectedExecutionHandler设置饱和策略。

ThreadPoolExecutor中包含了四种饱和策略:

AbortPolicy, DiscardPolicyDiscardOldestPolicyCallerRunsPolicy

AbortPolicy,即中止策略,是默认的饱和策略,该策略将抛出未检查的RejectedExecutionException

DiscardPolicy,即抛弃策略,会丢弃队列满后请求的任务。

DiscardOldestPolicy,即抛弃最旧的策略,会抛弃下一个将要被执行的任务,然后尝试重新提交新任务。

CallerRunsPolicy即调用者策略,既不会抛弃任务,也不会抛出异常,而是将任务回退到调用者。它不会在线程池的某个线程执行新提交的任务,而是在一个调用execute的线程中执行该任务。


注意:

1、可以利用ArrayBlockingQueue,有界的LinkedBlockingQueuePriorityBlockingQueue来设置存储任务的队列界限。

2、利用Executors静态工厂方法创建ThreadPoolExecutor或者直接实例化ThreadPoolExecutor的对象,默认初始的时候线程并不会立即启动,而是等到有任务提交时候才会启动。当然可以调用prestartAllCoreThreads来启动所有的核心线程。

3、在使用线程池中,当任务是相互独立且类型基本上相同的时候,此时才可以设置线程池和工作队列的界限。




本文转自 zhao_xiao_long 51CTO博客,原文链接:http://blog.51cto.com/computerdragon/1212442


相关文章
|
14天前
|
Java 调度
Java并发编程:深入理解线程池的原理与实践
【4月更文挑战第6天】本文将深入探讨Java并发编程中的重要概念——线程池。我们将从线程池的基本原理入手,逐步解析其工作过程,以及如何在实际开发中合理使用线程池以提高程序性能。同时,我们还将关注线程池的一些高级特性,如自定义线程工厂、拒绝策略等,以帮助读者更好地掌握线程池的使用技巧。
|
21天前
|
Java 程序员
java线程池讲解面试
java线程池讲解面试
39 1
|
16天前
|
Java
深入理解Java并发编程:线程池的应用与优化
【4月更文挑战第3天】 在Java并发编程中,线程池是一种重要的资源管理工具,它能有效地控制和管理线程的数量,提高系统性能。本文将深入探讨Java线程池的工作原理、应用场景以及优化策略,帮助读者更好地理解和应用线程池。
|
12天前
|
Java
Java 并发编程:深入理解线程池
【4月更文挑战第8天】本文将深入探讨 Java 中的线程池技术,包括其工作原理、优势以及如何使用。线程池是 Java 并发编程的重要工具,它可以有效地管理和控制线程的执行,提高系统性能。通过本文的学习,读者将对线程池有更深入的理解,并能在实际开发中灵活运用。
|
1月前
|
监控 Java
Java并发编程中的线程池优化技巧
在Java并发编程中,线程池扮演着至关重要的角色。本文将深入探讨如何优化Java线程池,从线程池的创建与配置、任务队列的选择、拒绝策略的制定、线程池状态的监控等多个方面进行详细阐述。通过本文的阅读,您将了解到如何合理地利用线程池,提高系统的并发性能,从而更好地应对各种并发场景。
|
12天前
|
Java
Java并发编程:深入理解线程池
【4月更文挑战第7天】在现代软件开发中,多线程编程已经成为一种不可或缺的技术。为了提高程序性能和资源利用率,Java提供了线程池这一强大工具。本文将深入探讨Java线程池的原理、使用方法以及如何根据实际需求定制线程池,帮助读者更好地理解和应用线程池技术。
15 0
|
2天前
|
缓存 分布式计算 监控
Java并发编程:深入理解线程池
【4月更文挑战第17天】在Java并发编程中,线程池是一种非常重要的技术,它可以有效地管理和控制线程的执行,提高系统的性能和稳定性。本文将深入探讨Java线程池的工作原理,使用方法以及在实际开发中的应用场景,帮助读者更好地理解和使用Java线程池。
|
3天前
|
存储 缓存 监控
Java线程池
Java线程池
31 1
|
3天前
|
缓存 监控 Java
Java并发编程:线程池与任务调度
【4月更文挑战第16天】Java并发编程中,线程池和任务调度是核心概念,能提升系统性能和响应速度。线程池通过重用线程减少创建销毁开销,如`ThreadPoolExecutor`和`ScheduledThreadPoolExecutor`。任务调度允许立即或延迟执行任务,具有灵活性。最佳实践包括合理配置线程池大小、避免过度使用线程、及时关闭线程池和处理异常。掌握这些能有效管理并发任务,避免性能瓶颈。
|
10天前
|
Java
Java并发编程:深入理解线程池
【4月更文挑战第10天】本文将深入探讨Java并发编程中的一个重要主题——线程池。我们将从线程池的基本概念入手,逐步深入到线程池的实现原理,以及如何在实际开发中合理使用线程池。通过本文的学习,你将能够理解线程池的核心原理,掌握线程池的使用技巧,以及避免常见的线程池使用误区。