「java.util.concurrent并发包」之 ThreadPool

一 异步用new Thread? 大些的"low"!!

new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
}
}).start();

你还在上面这么用吗,太low 了。弊端多多:

1.  每次new Thread新建对象性能差。

2. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。

3. 缺乏更多功能,如定时执行、定期执行、线程中断。

相比new Thread,Java提供的四种线程池的好处与此相对,在于:
1. 重用存在的线程,减少对象创建、消亡的开销,性能佳。

2. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。

3. 提供定时执行、定期执行、单线程、并发数控制等功能。

二 四种线程池比较

Java通过Executors提供四种线程池,分别为:

newCachedThreadPool      创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程(60s不执行任务),若无可回收,则新建线程。
newFixedThreadPool         创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。 
newScheduledThreadPool  创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor  创建一个单线程化的线程池,它只会用唯一的工作线程来串行执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

三 底层java.util.concurrent.ThreadPoolExecutor

无论创建哪种线程池 必须要调用ThreadPoolExecutor, ??列举一个构造方法

当一个任务通过execute(Runnable)方法欲添加到线程池时:

如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。 
如果此时线程池中的数量等于corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。 
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。 
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。

也就是:处理任务的优先级为: 
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

拒绝策略:

四 ThreadPoolExecutor源码分析

1. FixedThreadPool

  • FixedThreadPool的corePoolSize和maxiumPoolSize都被设置为创建FixedThreadPool时指定的参数nThreads。
  • 0L则表示当线程池中的线程数量操作核心线程的数量时,多余的线程将被立即停止
  • 最后一个参数表示FixedThreadPool使用了无界队列LinkedBlockingQueue作为线程池的做工队列,由于是无界的,当线程池的线程数达到corePoolSize后,新任务将在无界队列中等待,因此线程池的线程数量不会超过corePoolSize,同时maxiumPoolSize也就变成了一个无效的参数,并且运行中的线程池并不会拒绝任务。
  • FixedThreadPool运行图如下

    执行过程如下:

    1.如果当前工作中的线程数量少于corePool的数量,就创建新的线程来执行任务。

    2.当线程池的工作中的线程数量达到了corePool,则将任务加入LinkedBlockingQueue。

    3.线程执行完1中的任务后会从队列中去任务。

    注意LinkedBlockingQueue是无界队列,所以可以一直添加新任务到线程池。

2. SingleThreadExecutor  

SingleThreadExecutor是使用单个worker线程的Executor。特点是使用单个工作线程执行任务。它的构造源码如下:

SingleThreadExecutor的corePoolSize和maxiumPoolSize都被设置1。

其他参数均与FixedThreadPool相同,其运行图如下:

执行过程如下:

1.如果当前工作中的线程数量少于corePool的数量,就创建一个新的线程来执行任务。

2.当线程池的工作中的线程数量达到了corePool,则将任务加入LinkedBlockingQueue。

3.线程执行完1中的任务后会从队列中去任务。

注意:由于在线程池中只有一个工作线程,所以任务可以按照添加顺序执行。

3. CachedThreadPool

CachedThreadPool是一个”无限“容量的线程池,它会根据需要创建新线程。特点是可以根据需要来创建新的线程执行任务,没有特定的corePool。下面是它的构造方法:

CachedThreadPool的corePoolSize被设置为0,即corePool为空;maximumPoolSize被设置为Integer.MAX_VALUE,即maximum是无界的。这里keepAliveTime设置为60秒,意味着空闲的线程最多可以等待任务60秒,否则将被回收。

CachedThreadPool使用没有容量的SynchronousQueue作为主线程池的工作队列,它是一个没有容量的阻塞队列。每个插入操作必须等待另一个线程的对应移除操作。这意味着,如果主线程提交任务的速度高于线程池中处理任务的速度时,CachedThreadPool会不断创建新线程。极端情况下,CachedThreadPool会因为创建过多线程而耗尽CPU资源。其运行图如下:

执行过程如下:

1.首先执行SynchronousQueue.offer(Runnable task)。如果在当前的线程池中有空闲的线程正在执行SynchronousQueue.poll(),那么主线程执行的offer操作与空闲线程执行的poll操作配对成功,主线程把任务交给空闲线程执行。,execute()方法执行成功,否则执行步骤2

2.当线程池为空(初始maximumPool为空)或没有空闲线程时,配对失败,将没有线程执行SynchronousQueue.poll操作。这种情况下,线程池会创建一个新的线程执行任务。

3.在创建完新的线程以后,将会执行poll操作。当步骤2的线程执行完成后,将等待60秒,如果此时主线程提交了一个新任务,那么这个空闲线程将执行新任务,否则被回收。因此长时间不提交任务的CachedThreadPool不会占用系统资源。

SynchronousQueue是一个不存储元素阻塞队列,每次要进行offer操作时必须等待poll操作,否则不能继续添加元素。

4. 手动调用new ThreadPoolExecutor

时间: 2024-11-06 13:07:43

「java.util.concurrent并发包」之 ThreadPool的相关文章

「java.util.concurrent并发包」之 ReentrantReadWriteLock

一 引言 在多线程的环境下,对同一份数据进行读写,会涉及到线程安全的问题.比如在一个线程读取数据的时候,另外一个线程在写数据,而导致前后数据的不一致性:一个线程在写数据的时候,另一个线程也在写,同样也会导致线程前后看到的数据的不一致性.这时候可以在读写方法中加入互斥锁,任何时候只能允许一个线程的一个读或写操作,而不允许其他线程的读或写操作,这样是可以解决这样以上的问题,但是效率却大打折扣了.因为在真实的业务场景中,一份数据,读取数据的操作次数通常高于写入数据的操作,而线程与线程间的读读操作是不涉

Java学习笔记—多线程(java.util.concurrent并发包概括,转载)

一.描述线程的类:Runable和Thread都属于java.lang包 二.内置锁synchronized属于jvm关键字,内置条件队列操作接口Object.wait()/notify()/notifyAll()属于java.lang包 二.提供内存可见性和防止指令重排的volatile属于jvm关键字 四.而java.util.concurrent包(J.U.C)中包含的是java并发编程中有用的一些工具类,包括几个部分: 1.locks部分:包含在java.util.concurrent.

java.util.concurrent并发包诸类概览

java.util.concurrent包的类都来自于JSR-166:Concurrent Utilities,官方的描述叫做“The JSR proposes a set of medium-level utilities that provide functionality commonly needed in concurrent programs. ”.作者是大名鼎鼎的Doug Lea,这个包的前身可以在这里找到,它最好的文档就是系统的API手册. 当然,这里参考的concurrent包

java多线程---------java.util.concurrent并发包----------ThreadPoolExecutor

ThreadPoolExecutor线程池 一.三个构造方法 ThreadPoolExecutor(int corePoolSize,int MaxmumPoolSize,long KeepAliveTime,,TimeUnit  unit,BolokingQueue<Runnable> workQueue) ThreadPoolExecutor(int corePoolSize,int MaxmumPoolSize,long KeepAliveTime,,TimeUnit  unit,Bol

java.util.concurrent.locks lock锁【2】

Lock 锁介绍 JDK1.5以前,我们实现线程同步都是通过synchroized关键字进行方法或者语句块锁定,以保证该关键字作用域内的操作都是原子性操作. JDK1.5以后,提供的并发包提供了更强大的功能和更为灵活,最为关键的是需要手工释放锁,需要unlock必须在finally方法内.这是非常值得注意的事情. 介绍一下Lock接口.实现类有3个,分别是 普通锁,读写锁-写锁,读写锁-读锁. API文档给出了相近的说明和demo. Lock l = ...; l.lock(); try { /

synchronized和java.util.concurrent.locks.Lock的区别

在看ConcurrentHashMap 源码的时候看到lock这个锁机制,不明白它和Synchronized的区别,查了一些资料记录下来,在Lock的文档中,对Lock的解释是:Lock实现比synchronized 提供了更多额外的锁操作,它有更灵活的结构,可以支持不同的属性,可以支持多个相关条件的对象.那下边看一下Lock可以提供哪些比synchronized 额外的操作,也就是解决synchronized 存在的问题: 1.它无法中断一个正在等候获得锁的线程 2.也无法通过投票得到锁,如果

java.util.concurrent包

在JavaSE5中,JUC(java.util.concurrent)包出现了 在java.util.concurrent包及其子包中,有了很多好玩的新东西: 1.执行器的概念和线程池的实现.Executor.ExecutorService框架 从Executor接口开始,到ExecutorService,再到很多基于ThreadPoolExecutor实现的具体执行器.执行器实际上是采用了一种叫做命令模式的设计,将任务Runnable和具体执行线程相分离,并给出了生命周期等管理方法,一般只要e

Java学习笔记—多线程(并发工具类,java.util.concurrent.atomic包)

在JDK的并发包里提供了几个非常有用的并发工具类.CountDownLatch.CyclicBarrier和Semaphore工具类提供了一种并发流程控制的手段,Exchanger工具类则提供了在线程间交换数据的一种手段.本章会配合一些应用场景来介绍如何使用这些工具类. CountDownLatch CountDownLatch允许一个或多个线程等待其他线程完成操作.假如有这样一个需求:我们需要解析一个Excel里多个sheet的数据,此时可以考虑使用多线程,每个线程解析一个sheet里的数据,

高并发第八弹:J.U.C起航(java.util.concurrent)

java.util.concurrent是JDK自带的一个并发的包主要分为以下5部分: 并发工具类(tools) 显示锁(locks) 原子变量类(aotmic) 并发集合(collections) Executor线程执行器 我们今天就说说 并发集合,除开 Queue,放在线程池的时候讲 先介绍以下 CopyOnWrite: Copy-On-Write简称COW,是一种用于程序设计中的优化策略.其基本思路是,从一开始大家都在共享同一个内容,当某个人想要修改这个内容的时候,才会真正把内容Copy