跟我学Java多线程——线程池与堵塞队列
前言
上一篇文章中我们将ThreadPoolExecutor进行了深入的学习和介绍,实际上我们在项目中应用的时候非常少有直接应用ThreadPoolExecutor来创建线程池的。在jdk的api中有这么一句话“可是,强烈建议程序猿使用较为方便的
Executors 工厂方法Executors.newCachedThreadPool()(无界线程池,能够进行自己主动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景提前定义了设置。
”所以这篇文章我们继续学习其他几种线程池。
线程池分类
newCachedThreadPool()
创建一个可缓存的线程池。即这个线程池是无界线程池。无界指工作线程的创建数量差点儿没有限制(事实上也有限制的,数目为Interger.MAX_VALUE),这样能够灵活的往线程池中加入数据;能够进行自己主动线程回收指的是假设长时间没有往线程池中提交任务。即假设工作线程空暇了指定的时间,则该工作线程将自己主动终止。终止后。假设你又提交了新的任务。则线程池又一次创建一个工作线程。
我们一般使用例如以下代码进行创建:
ExecutorServiceservice = Executors.newCachedThreadPool();
我们点击代码进入源代码:
/**
* Creates a thread pool that creates newthreads as needed, but
* will reuse previously constructedthreads when they are
* available. These pools will typically improve theperformance
* of programs that execute manyshort-lived asynchronous tasks.
* Calls to {@code execute} will reusepreviously constructed
* threads if available. If no existingthread is available, a new
* thread will be created and added to thepool. Threads that have
* not been used for sixty seconds areterminated and removed from
* the cache. Thus, a pool that remainsidle for long enough will
* not consume any resources. Note thatpools with similar
* properties but different details (forexample, timeout parameters)
* may be created using {@linkThreadPoolExecutor} constructors.
*
* @return the newly created thread pool
*/
public static ExecutorServicenewCachedThreadPool() {
return new ThreadPoolExecutor(0,Integer.MAX_VALUE,
60L,TimeUnit.SECONDS,
newSynchronousQueue<Runnable>());
}
看到代码有没有非常熟悉。调用的上我们上一篇文章中的ThreadPoolExecutor类的构造方法,仅仅只是核心线程数为0。同一时候指定一个最大线程数。
newFixedThreadPool(int)
固定大小线程池这个非常好理解。就是创建一个指定工作线程数量的线程池,假设线程达到设置的最大数。就将提交的任务放到线程池的队列中。一个典型且优秀的线程池,它具有线程池提高程序效率和节省创建线程时所耗的开销的长处。
但在线程池空暇时,即线程池中没有可执行任务时,它不会释放工作线程,还会占用一定的系统资源。
一般创建:
ExecutorServicenewFixedThreadPool=Executors.newFixedThreadPool(5);
点击进入源代码:
/**
* Creates a thread pool that reuses afixed number of threads
* operating off a shared unboundedqueue. At any point, at most
* {@code nThreads} threads will be activeprocessing tasks.
* If additional tasks are submitted whenall threads are active,
* they will wait in the queue until athread is available.
* If any thread terminates due to afailure during execution
* prior to shutdown, a new one will takeits place if needed to
* execute subsequent tasks. The threads in the pool will exist
* until it is explicitly {@linkExecutorService#shutdown shutdown}.
*
* @param nThreads the number of threads inthe pool
* @return the newly created thread pool
* @throws IllegalArgumentException if{@code nThreads <= 0}
*/
public static ExecutorServicenewFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads,nThreads,
0L,TimeUnit.MILLISECONDS,
newLinkedBlockingQueue<Runnable>());
}
调用的依旧是我们上一篇文章中的ThreadPoolExecutor类的构造方法,仅仅只是核心线程数为和最大线程数一样都是我们人为指定的。
newSingleThreadExecutor()
单线程线程池,仅仅创建唯一的线程来运行任务。假设这个线程异常结束。会有还有一个代替它,保证顺序运行。
一般创建方法:
ExecutorServicenewSingleThreadExecutor = Executors.newSingleThreadExecutor();
点击进入源代码:
/**
* Creates an Executor that uses a singleworker thread operating
* off an unbounded queue. (Note howeverthat if this single
* thread terminates due to a failureduring execution prior to
* shutdown, a new one will take its placeif needed to execute
* subsequent tasks.) Tasks are guaranteed to execute
* sequentially, and no more than one taskwill be active at any
* given time. Unlike the otherwiseequivalent
* {@code newFixedThreadPool(1)} thereturned executor is
* guaranteed not to be reconfigurable touse additional threads.
*
* @return the newly createdsingle-threaded Executor
*/
public static ExecutorServicenewSingleThreadExecutor() {
return newFinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L,TimeUnit.MILLISECONDS,
newLinkedBlockingQueue<Runnable>()));
}
调用的依旧是我们上一篇文章中的ThreadPoolExecutor类的构造方法,仅仅只是核心线程数为和最大线程数一样都是1。
介绍到这里我们发现这三个线程池调用的都是ThreadPoolExecutor的构造函数。这三个线程的差别除了核心线程数和最大线程数參数不一样外,最重要的是传入的最后一个參数即workQueue是不一样的。
newCachedThreadPool的參数为SynchronousQueue,newFixedThreadPool和newSingleThreadExecutor的參数都为LinkedBlockingQueue。事实上这一种排队策略也叫堵塞队列。那接下来我们就来介绍一下常见的堵塞队列。
堵塞队列BlockingQueue
堵塞队列顾名思义首先它是一个队列,常见的队列有“后进先出”的栈和“先进先出”的队列。多线程环境中,通过队列能够非常easy实现数据共享,最经典的就是“生产者”和“消费者”模型。这就是一个典型的堵塞队列,比方生产者生产到一定程度必须停一下,让生产者线程挂起,这就是堵塞。
在多线程领域:所谓堵塞,在某些情况下会挂起线程(即堵塞)。一旦条件满足。被挂起的线程又会自己主动被唤醒)
java.util.concurrent包中的BlockingQueue就是堵塞队列的接口,作为BlockingQueue的使用者,我们再也不须要关心什么时候须要堵塞线程。什么时候须要唤醒线程,由于这一切BlockingQueue都给你一手包办了,而且它还是线程安全的。那我们如今来看下BlockingQueue接口的源代码:
public interfaceBlockingQueue<E> extends Queue<E> { boolean add(E e); boolean offer(E e); void put(E e) throws InterruptedException; boolean offer(E e, long timeout, TimeUnitunit)
throws InterruptedException; E take() throws InterruptedException; E poll(long timeout, TimeUnit unit)
throws InterruptedException; int remainingCapacity(); boolean remove(Object o); public boolean contains(Object o); int drainTo(Collection<? super E> c); int drainTo(Collection<? super E> c,int maxElements);
}
上面就是接口的全部方法,如今我们就介绍下这个接口中的核心方法:
放入数据:
boolean add(E e);
这种方法将将泛型对象加到BlockingQueue里,即假设BlockingQueue能够容纳,则返回true,否则返回false.(本方法不堵塞当前运行方法的线程)
boolean offer(E e);
这种方法将将泛型对象加到BlockingQueue里,即假设BlockingQueue能够容纳,则返回true,否则返回false.(本方法不堵塞当前运行方法的线程)
boolean offer(E e, long timeout, TimeUnitunit)throws InterruptedException;
这种方法能够设定等待的时间,假设在指定的时间内,还不能往队列中增加BlockingQueue,则返回失败。(本方法不堵塞当前运行方法的线程)
void put(E e) throws InterruptedException;
这种方法把泛型对象放到BlockingQueue里,假设BlockQueue没有空间,则调用此方法的线程被阻断直到BlockingQueue里面有空间再继续.(本方法有堵塞的功能)
移除数据:
boolean remove(Object o);
这种方法从BlockingQueue取出一个队首的对象,假设在指定时间内,队列一旦有数据可取,则马上返回队列中的数据。否则知道时间超时还没有数据可取,返回失败。(本方法不堵塞当前运行方法的线程)
E poll(long timeout, TimeUnit unit) throws InterruptedException;
这种方法从BlockingQueue取出一个队首的对象,假设在指定时间内。队列一旦有数据可取,则马上返回队列中的数据。否则知道时间超时还没有数据可取。返回失败。(本方法不堵塞当前运行方法的线程)
E take() throws InterruptedException;
这种方法是取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到BlockingQueue有新的数据被增加;(本方法有堵塞的功能)
int drainTo(Collection<? super E> c);
这种方法是取走BlockingQueue里排在首位的对象,取不到时返回null;(本方法不堵塞当前运行方法的线程)
int drainTo(Collection<? super E> c,int maxElements);
这种方法是取走BlockingQueue里排在首位的对象,若不能马上取出,则能够等time參数规定的时间,取不到时返回null;(本方法不堵塞当前运行方法的线程)
总结一下BlockingQueue接口中的方法,这些方法以四种形式出现,对于不能马上满足但可能在将来某一时刻能够满足的操作,这四种形式的处理方式不同:第一种是抛出一个异常,另外一种是返回一个特殊值(null或 false,详细取决于操作),第三种是在操作能够成功前,无限期地堵塞当前线程,第四种是在放弃前仅仅在给定的最大时间限制内堵塞。
抛出异常 |
特殊值 |
堵塞 |
超时 |
|
插入 |
add(e) |
offer(e) |
put(e) |
offer(e,time,unit) |
移除 |
remove() |
poll() |
take() |
poll(time,unit) |
检查 |
element() |
peek() |
不可用 |
不可用 |
BlockingQueue实现类
1)ArrayBlockingQueue:基于数组实现的一个堵塞队列。在创建ArrayBlockingQueue对象时必须制定容量大小。以便缓存队列中数据对象。而且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的队列最优先可以訪问队列。
其所含的对象是以FIFO(先入先出)顺序排序的.
2)LinkedBlockingQueue:基于链表实现的一个堵塞队列。在创建LinkedBlockingQueue对象时假设不指定容量大小,则默认大小为Integer.MAX_VALUE。其所含的对象是以FIFO(先入先出)顺序排序的
3)PriorityBlockingQueue:类似于LinkedBlockQueue,但其所含对象的排序不是FIFO,它会依照元素的优先级对元素进行排序,依照优先级顺序出队,每次出队的元素都是优先级最高的元素。注意,此堵塞队列为无界堵塞队列,即容量没有上限(通过源代码就能够知道,它没有容器满的信号标志),前面2种都是有界队列。
4)DelayQueue:基于PriorityQueue,一种延时堵塞队列,DelayQueue中的元素仅仅有当其指定的延迟时间到了。才可以从队列中获取到该元素。DelayQueue也是一个无界队列,因此往队列中插入数据的操作(生产者)永远不会被堵塞,而仅仅有获取数据的操作(消费者)才会被堵塞。
5)SynchronousQueue:一种无缓冲的等待队列,类似于无中介的直接交易。
当中LinkedBlockingQueue和ArrayBlockingQueue比較起来,它们背后所用的数据结构不一样,导致LinkedBlockingQueue的数据吞吐量要大于ArrayBlockingQueue,但在线程数量非常大时其性能的可预见性低于ArrayBlockingQueue.
总结
我们这篇文章延续了上一篇文章中关于ThreadPoolExecutor线程池的一些内容,各自是newCachedThreadPool、newFixedThreadPool、newSingleThreadExecutor,同一时候依据这些线程池与ThreadPoolExecutor的关系。进而引出了堵塞队列BlockingQueue。于是我们具体介绍了接口BlockingQueue和接口中的方法,最后又介绍了接口BlockingQueue的实现类。
跟我学Java多线程——线程池与堵塞队列的更多相关文章
- java多线程--线程池的使用
程序启动一个新线程的成本是很高的,因为涉及到要和操作系统进行交互,而使用线程池可以很好的提高性能,尤其是程序中当需要创建大量生存期很短的线程时,应该优先考虑使用线程池. 线程池的每一个线程执行完毕后, ...
- java多线程-线程池
线程池(Thread Pool)对于限制应用程序中同一时刻运行的线程数很有用.因为每启动一个新线程都会有相应的性能开销,每个线程都需要给栈分配一些内存等等. 我们可以把并发执行的任务传递给一个线程池, ...
- Java多线程——线程池
系统启动一个新线程的成本是比较高的,因为它涉及到与操作系统的交互.在这种情况下,使用线程池可以很好的提供性能,尤其是当程序中需要创建大量生存期很短暂的线程时,更应该考虑使用线程池. 与数据库连接池类似 ...
- java多线程——线程池源码分析(一)
本文首发于cdream的个人博客,点击获得更好的阅读体验! 欢迎转载,转载请注明出处. 通常应用多线程技术时,我们并不会直接创建一个线程,因为系统启动一个新线程的成本是比较高的,涉及与操作系统的交互, ...
- java多线程----线程池源码分析
http://www.cnblogs.com/skywang12345/p/3509954.html 线程池示例 在分析线程池之前,先看一个简单的线程池示例. 1 import java.util.c ...
- [Java多线程]-线程池的基本使用和部分源码解析(创建,执行原理)
前面的文章:多线程爬坑之路-学习多线程需要来了解哪些东西?(concurrent并发包的数据结构和线程池,Locks锁,Atomic原子类) 多线程爬坑之路-Thread和Runable源码解析 多线 ...
- 深入理解Java多线程——线程池
目录 为什么需要线程池 定义 ThreadPoolExecutor 工作队列workQueue 不同的线程池 Executor 线程池的工作原理 线程池生命周期 线程池增长策略 线程池大小的设置 线程 ...
- Java多线程-线程池ThreadPoolExecutor构造方法和规则
为什么用线程池 原文地址 http://blog.csdn.net/qq_25806863/article/details/71126867 有时候,系统需要处理非常多的执行时间很短的请求,如果每一个 ...
- Java多线程-----线程池详解
1. 线程池的实现原理 提交一个任务到线程池中,线程池的处理流程如下: 判断线程池里的核心线程是否都在执行任务,如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务.如果 ...
随机推荐
- 73. 解决ExtJS TreePanel 的 iconCls设置问题
转自:https://blog.csdn.net/hanchuang213/article/details/62881568 很久没有写代码了,最近在做一个在线帮助网站,于是又捡起了 ExtJS,我用 ...
- Spark深入之RDD
目录 Part III. Low-Level APIs Resilient Distributed Datasets (RDDs) 1.介绍 2.RDD代码 3.KV RDD 4.RDD Join A ...
- C# List常识之经常被忽略的常识
最近在接收前辈的代码,越来越会发现有很多.net已经封装好的方法可以使用,我们却不知道,然后自己去For/Foreach循环解决自己的需求问题 总的来说:当下很忧伤啊.总结了几个经常需要用却不知道的方 ...
- Java 网络IO编程(BIO、NIO、AIO)
本概念 BIO编程 传统的BIO编程 代码示例: public class Server { final static int PROT = 8765; public static void main ...
- Java.Cas4.0客户端接入配置
1.本示例中使用cas4.0+cas-client-core3.4版本,ide则是myeclipse2014 blue版本,mvn使用3.9版本 2.注意事项:请先实现破解cas-client-cor ...
- HBase、Hive、MapReduce、Hadoop、Spark 开发环境搭建后的一些步骤(export导出jar包方式 或 Ant 方式)
步骤一 若是,不会HBase开发环境搭建的博文们,见我下面的这篇博客. HBase 开发环境搭建(Eclipse\MyEclipse + Maven) 步骤一里的,需要补充的.如下: 在项目名,右键, ...
- Laravel5.1学习笔记7 视图
视图 (View) 基本用法 传递数据到视图 在多个视图中分享数据 视图组件 #基本用法 视图里面包含了你应用程序所提供的 HTML 代码,并且提供一个简单的方式来分离控制器和网页呈现上的逻辑.视 ...
- Android 复制文本内容到系统剪贴板(自由复制)
直接上代码:(对应的类:android.content.ClipboardManager) //获取剪贴板管理器: ClipboardManager cm = (ClipboardManager) g ...
- 1、Visual Studio Code安装及Hello Word
一.环境初始化 1.下载 Visual Studio Code对应版本安装 2.下载.NET Core 2.0 SDK安装 3.安装Mono Debug 完成后界面如下: 二.创建控制 ...
- DeltaFish 校园物资共享平台 第一次小组会议
软工小组第一次会议 会议地点:图书馆 会议时间:19:00 ~ 20:00 与会人员:软工小组全体成员 请假人员:无缺席人员:无 记录人:陈志锴 整理人:曾子轩 会议记录 一.确认选题 每一位成员提出 ...