java 线程池

来源:互联网 发布:php长连接 消息推送 编辑:程序博客网 时间:2024/06/07 23:33

本篇主要涉及到的是java.util.concurrent包中的ExecutorService。ExecutorService就是Java中对线程池的实现。

一、ExecutorService介绍

ExecutorService是Java中对线程池定义的一个接口,它java.util.concurrent包中,在这个接口中定义了和后台任务执行相关的方法: 
这里写图片描述

Java API对ExecutorService接口的实现有两个,所以这两个即是Java线程池具体实现类(详细了解这两个实现类,点击这里):

1. ThreadPoolExecutor2. ScheduledThreadPoolExecutor

除此之外,ExecutorService还继承了Executor接口(注意区分Executor接口和Executors工厂类),这个接口只有一个execute()方法,最后我们看一下整个继承树: 

这里写图片描述

二、ExecutorService的创建

创建一个什么样的ExecutorService的实例(即线程池)需要g根据具体应用场景而定,不过Java给我们提供了一个Executors工厂类,它可以帮助我们很方便的创建各种类型ExecutorService线程池,Executors一共可以创建下面这四类线程池:

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

备注:Executors只是一个工厂类,它所有的方法返回的都是ThreadPoolExecutorScheduledThreadPoolExecutor这两个类的实例。

三、ExecutorService的使用

ExecutorService executorService = Executors.newFixedThreadPool(10);executorService.execute(new Runnable() {public void run() {    System.out.println("Asynchronous task");}});executorService.shutdown();

四、ExecutorService的执行

ExecutorService有如下几个执行方法:

- execute(Runnable)- submit(Runnable)- submit(Callable)- invokeAny(...)- invokeAll(...)

4.1 execute(Runnable)

这个方法接收一个Runnable实例,并且异步的执行,请看下面的实例:

ExecutorService executorService = Executors.newSingleThreadExecutor();executorService.execute(new Runnable() {public void run() {    System.out.println("Asynchronous task");}});executorService.shutdown();

这个方法有个问题,就是没有办法获知task的执行结果。如果我们想获得task的执行结果,我们可以传入一个Callable的实例(下面会介绍)。

4.2 submit(Runnable)

submit(Runnable)execute(Runnable)区别是前者可以返回一个Future对象,通过返回的Future对象,我们可以检查提交的任务是否执行完毕,请看下面执行的例子:

Future future = executorService.submit(new Runnable() {public void run() {    System.out.println("Asynchronous task");}});future.get();  //returns null if the task has finished correctly.

如果任务执行完成,future.get()方法会返回一个null。注意,future.get()方法会产生阻塞。

4.3 submit(Callable)

submit(Callable)submit(Runnable)类似,也会返回一个Future对象,但是除此之外,submit(Callable)接收的是一个Callable的实现,Callable接口中的call()方法有一个返回值,可以返回任务的执行结果,而Runnable接口中的run()方法是void的,没有返回值。请看下面实例:

Future future = executorService.submit(new Callable(){public Object call() throws Exception {    System.out.println("Asynchronous Callable");    return "Callable Result";}});System.out.println("future.get() = " + future.get());

如果任务执行完成,future.get()方法会返回Callable任务的执行结果。注意,future.get()方法会产生阻塞。

4.4 invokeAny(…)

invokeAny(...)方法接收的是一个Callable的集合,执行这个方法不会返回Future,但是会返回所有Callable任务中其中一个任务的执行结果。这个方法也无法保证返回的是哪个任务的执行结果,反正是其中的某一个。请看下面实例:

ExecutorService executorService = Executors.newSingleThreadExecutor();Set<Callable<String>> callables = new HashSet<Callable<String>>();callables.add(new Callable<String>() {public String call() throws Exception {    return "Task 1";}});callables.add(new Callable<String>() {public String call() throws Exception {    return "Task 2";}});callables.add(new Callable<String>() {    public String call() throws Exception {    return "Task 3";}});String result = executorService.invokeAny(callables);System.out.println("result = " + result);executorService.shutdown();

大家可以尝试执行上面代码,每次执行都会返回一个结果,并且返回的结果是变化的,可能会返回“Task2”也可是“Task1”或者其它。

4.5 invokeAll(…)

invokeAll(...) invokeAny(...)类似也是接收一个Callable集合,但是前者执行之后会返回一个Future的List,其中对应着每个Callable任务执行后的Future对象。情况下面这个实例:

ExecutorService executorService = Executors.newSingleThreadExecutor();Set<Callable<String>> callables = new HashSet<Callable<String>>();callables.add(new Callable<String>() {public String call() throws Exception {    return "Task 1";}});callables.add(new Callable<String>() {    public String call() throws Exception {    return "Task 2";}});callables.add(new Callable<String>() {public String call() throws Exception {    return "Task 3";}});List<Future<String>> futures = executorService.invokeAll(callables);for(Future<String> future : futures){System.out.println("future.get = " + future.get());}executorService.shutdown();

五、ExecutorService的关闭

当我们使用完成ExecutorService之后应该关闭它,否则它里面的线程会一直处于运行状态。

举个例子,如果的应用程序是通过main()方法启动的,在这个main()退出之后,如果应用程序中的ExecutorService没有关闭,这个应用将一直运行。之所以会出现这种情况,是因为ExecutorService中运行的线程会阻止JVM关闭。

如果要关闭ExecutorService中执行的线程,我们可以调用ExecutorService.shutdown()方法。在调用shutdown()方法之后,ExecutorService不会立即关闭,但是它不再接收新的任务,直到当前所有线程执行完成才会关闭,所有在shutdown()执行之前提交的任务都会被执行。

如果我们想立即关闭ExecutorService,我们可以调用ExecutorService.shutdownNow()方法。这个动作将跳过所有正在执行的任务和被提交还没有执行的任务。但是它并不对正在执行的任务做任何保证,有可能它们都会停止,也有可能执行完成。

六、ThreadPoolExecutor

ThreadPoolExecutor是ExecutorService的一个实现类,也是java中最常用的线程池类。ThreadPoolExecutor内部维持了一个线程池,可以执行给定的任务,下面是关于它的具体使用方法。

1.1 ThreadPoolExecutor构造方法及其作用

ThreadPoolExecutor源码中的构造方法:

这里写图片描述

- corePoolSize:线程池维护线程的最少数量- maximumPoolSize:线程池维护线程的最大数量- keepAliveTime: 线程池维护线程所允许的空闲时间- unit: 线程池维护线程所允许的空闲时间的单位- workQueue: 线程池所使用的缓冲队列- handler: 线程池对拒绝任务的处理策略

1.2 线程数量控制

ThreadPoolExecutor线程池中的线程数量是可变的,其变化范围取决于下面两个变量:

1. corePoolSize:线程池维护线程的最少数量2. maximumPoolSize:线程池维护线程的最大数量

具体线程的分配方式是,当一个任务被添加到线程池:

1. 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。2. 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。3. 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。4. 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。5. 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。

这样,线程池可以动态的调整池中的线程数。除了corePoolSizemaximumPoolSize两个变量外,ThreadPoolExecutor构造方法还有几个参数:

- keepAliveTime: 线程池维护线程所允许的空闲时间- unit: 线程池维护线程所允许的空闲时间的单位- workQueue: 线程池所使用的缓冲队列- handler: 线程池对拒绝任务的处理策略

1.2 unit

unit 可选的参数为java.util.concurrent.TimeUnit中的几个静态属性:

- NANOSECONDS- MICROSECONDS- MILLISECONDS- SECONDS

1.3 workQueue

workQueue是一个BlockingQueue,默认是LinkedBlockingQueue<Runnable>

1.4 handler

handler 是线程池拒绝处理任务的方式,主要有四种类型:

  1. ThreadPoolExecutor.AbortPolicy()(系统默认):抛出java.util.concurrent.RejectedExecutionException异常
  2. ThreadPoolExecutor.CallerRunsPolicy():当抛出RejectedExecutionException异常时,会调用rejectedExecution方法
  3. ThreadPoolExecutor.DiscardOldestPolicy():抛弃旧的任务
  4. ThreadPoolExecutor.DiscardPolicy():抛弃当前的任务

1.5 创建一个ThreadPoolExecutor

int  corePoolSize  =    5;int  maxPoolSize   =   10;long keepAliveTime = 5000;ExecutorService threadPoolExecutor =    new ThreadPoolExecutor(            corePoolSize,            maxPoolSize,            keepAliveTime,            TimeUnit.MILLISECONDS,                new LinkedBlockingQueue<Runnable>()            );

七、ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor是ExecutorService的另一个实现类,从上面Java线程池ExecutorService继承树这幅图可以看出,ScheduledThreadPoolExecutor直接继承自ScheduledExecutorService,ScheduledThreadPoolExecutor 类的功能也主要体现在ScheduledExecutorService 接口上,而所以在介绍ScheduledThreadPoolExecutor之前先介绍一下ScheduledExecutorService接口。

2.1 ScheduledExecutorService接口介绍

java.util.concurrent.ScheduledExecutorService接口继承了ExecutorService,它的最主要的功能就是可以对其中的任务进行调度,比如延迟执行、定时执行等等。

ScheduledExecutorService接口定义:

public interface ScheduledExecutorService extends ExecutorService {    public ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit);    public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay, long period, TimeUnit unit);    public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay, long delay,TimeUnit unit);}

从上面接口定义我们知道,提供了四个方法,下面我们就分别介绍:

1. schedule (Runnable task, long delay, TimeUnit timeunit)2. schedule (Callable task, long delay, TimeUnit timeunit)3. scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)4. scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit)

2.1.1 schedule (Runnable task, long delay, TimeUnit timeunit)

这个方法的意思是在指定延迟之后运行task。这个方法有个问题,就是没有办法获知task的执行结果。如果我们想获得task的执行结果,我们可以传入一个Callable的实例(后面会介绍)。

ScheduledExecutorService scheduledExecutorService =    Executors.newScheduledThreadPool(5);ScheduledFuture scheduledFuture =scheduledExecutorService.schedule(new Callable() {    public Object call() throws Exception {        System.out.println("Executed!");        return "Called!";    }},5,TimeUnit.SECONDS);System.out.println("result = " + scheduledFuture.get());scheduledExecutorService.shutdown();

2.1.2 schedule (Callable task, long delay, TimeUnit timeunit)

这个方法与schedule (Runnable task)类似,也是在指定延迟之后运行task,不过它接收的是一个Callable实例,此方法会返回一个ScheduleFuture对象,通过ScheduleFuture我们可以取消一个未执行的task,也可以获得这个task的执行结果。

ScheduledExecutorService scheduledExecutorService =    Executors.newScheduledThreadPool(5);ScheduledFuture scheduledFuture =scheduledExecutorService.schedule(new Callable() {    public Object call() throws Exception {        System.out.println("Executed!");        return "Called!";    }},5,TimeUnit.SECONDS);System.out.println("result = " + scheduledFuture.get());scheduledExecutorService.shutdown();

2.1.3 scheduleAtFixedRate (Runnable, long initialDelay, long period, TimeUnit timeunit)

这个方法的作用是周期性的调度task执行。task第一次执行的延迟根据initialDelay参数确定,以后每一次执行都间隔period时长。

如果task的执行时间大于定义的period,那么下一个线程将在当前线程完成之后再执行。整个调度保证不会出现一个以上任务同时执行。

2.1.4 scheduleWithFixedDelay (Runnable, long initialDelay, long period, TimeUnit timeunit)

scheduleWithFixedDelay的参数和scheduleAtFixedRate参数完全一致,它们的不同之处在于对period调度周期的解释。

在scheduleAtFixedRate中,period指的两个任务开始执行的时间间隔,也就是当前任务的开始执行时间和下个任务的开始执行时间之间的间隔。

而在scheduleWithFixedDelay中,period指的当前任务的结束执行时间到下个任务的开始执行时间。

2.1.5 ScheduledExecutorService的关闭

和ExecutorService类似, 我们在使用完ScheduledExecutorService时需要关闭它。如果不关闭的话,JVM会一直运行直,即使所有线程已经关闭了。

关闭ScheduledExecutorService可以使用其继承自ExecutorService接口的shutdown()shutdownNow()方法,两者的区别请参考【Java线程池 ExecutorService】。

2.2 ScheduledThreadPoolExecutor

ScheduledThreadPoolExecutor继承自ThreadPoolExecutor,构造参数很简单,只有3个:

1. int corePoolSize:线程池维护线程的最少数量2. ThreadFactory threadFactory:线程工程类,线程池用它来制造线程3. RejectedExecutionHandler handler:线程池对拒绝任务的处理策略

具体使用方法请参考ThreadPoolExecutor或者使用Executors。

八、Executors

创建一个什么样的ExecutorService的实例(即线程池)需要我们的具体应用场景而定,不过Java给我们提供了一个Executors工厂类,它可以帮助我们很方便的创建各种类型ExecutorService线程池,Executors一共可以创建下面这四类线程池:

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

备注:Executors只是一个工厂类,它所有的方法返回的都是ThreadPoolExecutor、ScheduledThreadPoolExecutor这两个类的实例。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 半个月小鸡屁骨下垂眼睛紧闭怎么办 在学校走廊把老师撞倒了怎么办 裤衩给孩子买的有点肥怎么办 到交警队立案后医疗费没了怎么办 交警扣车车里的贵重东西没了怎么办 睾丸内囊肿割了又长怎么办 我把别人的卵子踢碎了怎么办 两岁宝宝不小心被猫抓出血怎么办 两岁宝宝小蛋蛋肿了怎么办 吃大胺片过敏蛋皮又痛又痒怎么办 刚出生二十天宝宝蛋蛋有疝气怎么办 宝宝八个月蛋蛋一个没掉下来怎么办 引产23天同房内射肚子疼怎么办l 房东禁止养宠物如果养了怎么办 圆通快递退回但没有签收记录怎么办 信用卡没有收到又退回去了怎么办 新疆不给邮寄快递被退回运费怎么办 淘宝上已付款还在想留言怎么办 b超显示肾结石但x光看不到怎么办 洗脚让洗脚妹摸射精了怎么办 在新疆塔城干活不给工资怎么办 挨打了屁股肿的又大又硬怎么办 李贞将军有关电影小腿肌腱疼怎么办 朋友老婆老是背地里说我坏话怎么办 当你听到别人在背后说你时怎么办 分手了和前任是同事需要见面怎么办 和附近的人聊天他约你见面怎么办 微信约妹子发完红包拉黑了怎么办 户口本被前妻扣着不能结婚怎么办 部队当兵户口没注销退伍回去怎么办 没打疫苗的狗狗咬了我的嘴巴怎么办 新车被撞了还没上牌怎么办? 签了合同上两天班想辞职怎么办 刚到公司上班两天想辞职怎么办 签入职合同一天后就不想干了怎么办 入职材料交了第二天就想离职怎么办 宝宝40周多了还不想出来怎么办 工厂上班不到三个月不给辞工怎么办 单位辞职后不上班了保险怎么办 药流6个小时没流下来怎么办 小孩有口臭怎么办这几天不想吃饭