一 简述

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

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

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

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

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

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

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

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

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

二 ThreadPoolExecutor线程池

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

在利用Executors静态生成ThreadPoolExecutor的时候,都会在内部实例化一个ThreadPoolExecutor。在ThreadPoolExecutor内部都是用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来存储任务的ThreadPoolExecutor。SynchronousQueue是一种同步移交队列,任务的生产者线程直接将任务移交给任务的消费者线程即工作线程。对于很大的线程池,可以利用SynchronousQueue避免任务的排队。要想将一个元素放入该队列,必须有另一个线程正在等待接收这个元素。当一个新的任务提交后,如果没有线程在等待,且线程池的当前值大小小于最大值,一般ThreadPoolExecutor都会创建一个新的线程。否则就会调用饱和策略来处理。SynchronousQueue这种队列一般用于当线程池无界或者很大的时候采用,目的就是更快的提交任务,充分利用线程池中的工作线程。

Executors.newFixedThreadPool(n);

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

Executors.newSingleThreadExecutor();

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

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

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

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

AbortPolicy, DiscardPolicy,DiscardOldestPolicy和CallerRunsPolicy

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

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

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

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

注意:

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

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

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

本文出自 “在云端的追梦” 博客,请务必保留此出处http://computerdragon.blog.51cto.com/6235984/1212442

Java线程池Executors的更多相关文章

  1. 【Java 多线程】Java线程池类ThreadPoolExecutor、ScheduledThreadPoolExecutor及Executors工厂类

    Java中的线程池类有两个,分别是:ThreadPoolExecutor和ScheduledThreadPoolExecutor,这两个类都继承自ExecutorService.利用这两个类,可以创建 ...

  2. Java 线程池框架核心代码分析--转

    原文地址:http://www.codeceo.com/article/java-thread-pool-kernal.html 前言 多线程编程中,为每个任务分配一个线程是不现实的,线程创建的开销和 ...

  3. Java线程池使用说明

    Java线程池使用说明 转自:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1.4极 ...

  4. (转载)JAVA线程池管理

    平时的开发中线程是个少不了的东西,比如tomcat里的servlet就是线程,没有线程我们如何提供多用户访问呢?不过很多刚开始接触线程的开发攻城师却在这个上面吃了不少苦头.怎么做一套简便的线程开发模式 ...

  5. Java线程池的那些事

    熟悉java多线程的朋友一定十分了解java的线程池,jdk中的核心实现类为java.util.concurrent.ThreadPoolExecutor.大家可能了解到它的原理,甚至看过它的源码:但 ...

  6. 四种Java线程池用法解析

    本文为大家分析四种Java线程池用法,供大家参考,具体内容如下 http://www.jb51.net/article/81843.htm 1.new Thread的弊端 执行一个异步任务你还只是如下 ...

  7. Java线程池应用

    Executors工具类用于创建Java线程池和定时器. newFixedThreadPool:创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程.在任意点,在大多数 nThread ...

  8. Java线程池与java.util.concurrent

    Java(Android)线程池 介绍new Thread的弊端及Java四种线程池的使用,对Android同样适用.本文是基础篇,后面会分享下线程池一些高级功能. 1.new Thread的弊端执行 ...

  9. [转 ]-- Java线程池使用说明

    Java线程池使用说明 原文地址:http://blog.csdn.net/sd0902/article/details/8395677 一简介 线程的使用在java中占有极其重要的地位,在jdk1. ...

  10. 【Java线程池快速学习教程】

    1. Java线程池 线程池:顾名思义,用一个池子装载多个线程,使用池子去管理多个线程. 问题来源:应用大量通过new Thread()方法创建执行时间短的线程,较大的消耗系统资源并且系统的响应速度变 ...

随机推荐

  1. k8s pod挂载hostPath执行写时报错Permission denied

    关于hostPath的权限说明 最近项目中经常遇到pod中container挂载主机hostPath报错无权限问题: httpd@hostpath-volume:/test-volume$ touch ...

  2. 从cmake解决clion编译生成的可执行文件(.exe)不可执行的问题

    我这里没有显示报错,是直接闪退的情况,但是网上搜索的时候大多数是有报错弹窗的,运行报错提示为无法链接动态库.如下图: ![原图来自CSDN@LJY_kt11 一句话说清楚 原因是使用CLion编译的程 ...

  3. docker with GPU support

    自己总结的: nvidia-docker, 不支持windows,2019.10, nvidia-docker过时了,从docker 1903开始,安装一个nvidia-container-runti ...

  4. Unity 刚体 AddForce 的几种力类型

    今天在实现 2D 横版跳跃的时候,发现使用AddForce添加的力太突兀了,没有逐渐向上的过程,发现AddForce还有ForceMode mode参数 以下部分内容摘自Bing Copilot总结 ...

  5. CSS – Clip Path

    前言 我是在搞 1 side box-shadow 发现这个功能的. 平常很少做特效, 所以对这个好功能缺乏认识. 这篇大概记入一下先, 以后有认真用再补上细节. 参考 Youtube – Aweso ...

  6. SQL Server Aggregate Functions

    SUM 如果 row count = 0 返回的是 NULL 而不是 0 哦, 如果要 0 可以使用 ISNULL 来处理 如果其中一些 row 是 NULL, 那无所谓, 它只会 SUM 数字出来 ...

  7. cortex-m3 m4 异常机制

    文章写的很好,待整理 1.[STM32]HardFault问题详细分析及调试笔记 https://blog.csdn.net/m0_54916619/article/details/129979222 ...

  8. TEN Framework 入坑记

    TL;DR TEN Framework 最初叫 Astra,后改为 TEN,即 Transformative Extensions Network. 我第一次见到 TEN (那时还叫 Astra)是在 ...

  9. mysql用户相关操作(转载)

    mysql用户相关操作 一. 创建用户 命令: CREATE USER 'username'@'host' IDENTIFIED BY 'password'; 说明: username:你将创建的用户 ...

  10. iOS本地化NSLocalizedString的使用小结

    在iOS设备,包括iPhone和iPad是全球可用.显然,iOS用户都来自不同国家,说着不同的语言.为了提供出色的用户体验,你可能希望以多种语言提供您的应用程序.适应应用程序以支持特定语言的过程通常被 ...