ExecutorService

Java.util.concurrent.ExecutorService接口代表一种异步执行机制,它能够在后台执行任务。因此ExecutorService与thread pool是非常相似的。事实上,在java.util.package包中ExecutorService的具体实现就是一个线程池的具体实现。

ExcutorService 例子

下面是一个简单的例子

ExecutorService executorService = Executors.newFixedThreadPool(10);

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

首先,通过newFixedThreadPool()工厂方法创建一个ExecutorService的实例。这个方法创建了一个可以有10个线程执行任务的线程池。

第二,Runnable接口的匿名实现类作为参数被传递给execute()方法。Runable将会被ExecutorService中的一个线程来执行。

任务委托(Task Delegation)

下面的图片说明了一个线程委托一个任务给ExecutorService进行异步执行:



一旦,线程委托任务给ExecutorService,线程会独立任务的执行而继续自己之后的操作。

ExcutorService的使用说明

下面是委托任务给ExecutorService的一些不同的方式:

  • execute(Runnable)
  • submit(Runnable)
  • submit(Callable)
  • invokeAny(…)
  • invokeAll(…)

下面来逐个看看这些方法。

  • execute(Runnable)

execute(Runnable) 方法接受一个java.lang.Runable对象的实例,并异步执行之。下面是一个使用ExecutorService执行Runnable的例子:

ExecutorService executorService = Executors.newSingleThreadExecutor();

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

这种方式不能获得Runnable执行的结果,如果有这种需要,你将要使用Callable。

  • submit(Runnable)

submit(Runnable) 方法也接收一个Runnable接口的具体实现,并返回一个Future对象。Future对象可以用来检测Runable是否执行完成。

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

submit(Callable)方法与submit(Runnable)方法相似,除了接收的参数有所不同。Callable实例非常类似于Runnable,不同的是call方法可以返回一个结果,Runnable.run()方法不能返回一个结果(因为是void类型),就算线程执行完了,成功了future.get()也只是得到null

可以通过submit(Callable)方法返回的Future对象获取Callable的结果。下面是一个使用Callable的例子:

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

上面代码的输出结果是:

Asynchronous Callable
future.get() = Callable Result
  • invokeAny(…)

invokeAny()方法接收一个Callable对象或者Callable的子接口实例的集合作为参数,这个方法不会返回Future,但会返回集合中某一个Callable的结果。你不能确定你得到是哪个Callable的结果。只是已执行完成的Callable中的一个。

如果一个任务已经完成(或者抛出了异常),剩余的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();
  • 示例代码将会打印给定的Callable集合中一个Callable任务返回的结果。我尝试执行了多次,结果是变化的。有时候是“Task1”,有时候是“Task 2”等。

  • invokeAll(…)

invokeAll()接收一个Callable对象的集合作为参数,该方法会调用你传给他的集合中的所有Callable对象。InvokeAll()会返回一个Future对象的列表,通过这个列表你可以获取每一个Callable执行的结果。

一个任务可能会因为一个异常而结束,因此这时任务并不是真正意义上执行成功了。这在Future上是没有办法来判断的。

invokeAll()处理一个任务的容器(collection),并返回一个Future的容器。两个容器具有相同的结构,这里提交的任务容器列表和返回的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 Shutdown

当你是使用完ExecutorService后,你应该关闭它,使得线程不能持续运行。例如,你的应用程序从main()方法开始并且你的主线程退出应用程序,这时如果存在激活状态的ExecutorService,你的应用程序将仍然会保持运行。ExecutorService中激活的线程会阻止JVM关闭。

为了终止ExecutorService中的线程,你需要调用shutdown()方法。ExecutorService不会立即关闭,但是它也不会接受新的任务,直到它里面的所有线程都执行完毕,ExecutorService才会关闭。所有提交到ExecutorService中的任务会在调用shutdown()方法之前被执行。

如果你想立即关闭ExecutorService,你可以调用shutdownNow()方法。这将会尝试立即停止所有正在执行的任务,并且忽略所有提交的但未被处理的任务。对于正在执行的任务是不能确定的,也许它们停止了,也行它们执行直到结束。

方法说明

1、shutdown():停止接收新任务,原来的任务继续执行

英文原意:关闭,倒闭;停工。 这里的意思是 关闭线程池与使用数据库连接池一样,每次使用完毕后,都要关闭线程池。

1、停止接收新的submit的任务;

2、已经提交的任务(包括正在跑的和队列中等待的),会继续执行完成;

3、等到第2步完成后,才真正停止;

2、shutdownNow():停止接收新任务,原来的任务停止执行

1、跟 shutdown() 一样,先停止接收新submit的任务;

2、忽略队列里等待的任务;

3、尝试将正在执行的任务interrupt中断;

4、返回未执行的任务列表;

说明:它试图终止线程的方法是通过调用 Thread.interrupt() 方法来实现的,这种方法的作用有限,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt() 方法是无法中断当前的线程的。所以,shutdownNow() 并不代表线程池就一定立即就能退出,它也可能必须要等待所有正在执行的任务都执行完成了才能退出。但是大多数时候是能立即退出的。

3、awaitTermination(long timeOut, TimeUnit unit):当前线程阻塞

timeout 和 TimeUnit 两个参数,用于设定超时的时间及单位

当前线程阻塞,直到:

  • 等所有已提交的任务(包括正在跑的和队列中等待的)执行完;
  • 或者 等超时时间到了(timeout 和 TimeUnit设定的时间);
  • 或者 线程被中断,抛出InterruptedException

然后会监测 ExecutorService 是否已经关闭,返回true(shutdown请求后所有任务执行完毕)或false(已超时)

区别

1、shutdown() 和 shutdownNow() 的区别

shutdown() 只是关闭了提交通道,用submit()是无效的;而内部该怎么跑还是怎么跑,跑完再停。

shutdownNow() 能立即停止线程池,正在跑的和正在等待的任务都停下了。

2、shutdown() 和 awaitTermination() 的区别

shutdown() 后,不能再提交新的任务进去;但是 awaitTermination() 后,可以继续提交。

awaitTermination()是阻塞的,返回结果是线程池是否已停止(true/false);shutdown() 不阻塞。

总结

1、优雅的关闭,用 shutdown()

2、想立马关闭,并得到未执行任务列表,用shutdownNow()

3、优雅的关闭,并允许关闭声明后新任务能提交,用 awaitTermination()

4、关闭功能 【从强到弱】 依次是:shuntdownNow() > shutdown() > awaitTermination()

ThreadPoolExecutor

Java.util.concurrent.ThreadPoolExecutor类是ExecutorSerivce接口的具体实现。ThreadPoolExecutor使用线程池中的一个线程来执行给定的任务(Runnable或者Runnable)。

ThreadPoolExecutor内部的线程池包含不定数量的线程。池中线程的数量由下面的这些变量决定:

  • corePoolSize
  • maximumPoolSize

当一个任务委托给线程池执行,此时如果池线程中线程数少于corePoolSize,即使池中有空闲的线程,线程池中也会创建一个新的线程。

如果任务队列是满的,corePoolSize个线程或者更多的且少于maximumPoolSize的线程正在运行,也会创建一个新的线程来执行任务。

下面图释ThreadPoolExecutor这种原理:

创建ThreadPoolExecutor

ThreadPoolExecutor有多种构造函数。例如:

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

除非你需要显示的给ThreadPoolExecutor指定这些参数,通常使用java.util.concurrent.Executor类中的工厂方法来创建实例。

【Java多线程】ExecutorService和ThreadPoolExecutor的更多相关文章

  1. java多线程系列:ThreadPoolExecutor源码分析

    前言 这篇主要讲述ThreadPoolExecutor的源码分析,贯穿类的创建.任务的添加到线程池的关闭整个流程,让你知其然所以然.希望你可以通过本篇博文知道ThreadPoolExecutor是怎么 ...

  2. Java多线程-线程池ThreadPoolExecutor构造方法和规则

    为什么用线程池 原文地址 http://blog.csdn.net/qq_25806863/article/details/71126867 有时候,系统需要处理非常多的执行时间很短的请求,如果每一个 ...

  3. java中 ExecutorService,Executor,ThreadPoolExecutor的用法

    package com; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Executor; import ...

  4. java多线程系列:ThreadPoolExecutor

    ThreadPoolExecutor自定义线程池 开篇一张图(图片来自阿里巴巴Java开发手册(详尽版)),后面全靠编 好了要开始编了,从图片中就可以看到这篇博文的主题了,ThreadPoolExec ...

  5. java多线程ExecutorService

    1.new Thread的弊端 执行一个异步任务你还只是如下new Thread吗? new Thread(new Runnable() { @Override public void run() { ...

  6. java 多线程 一个博客

    http://blog.csdn.net/a352193394/article/category/2563875 Java多线程之~~~线程安全容器的非阻塞容器 在并发编程中,会经常遇到使用容器.但是 ...

  7. 转:java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例

    java多线程CountDownLatch及线程池ThreadPoolExecutor/ExecutorService使用示例 1.CountDownLatch:一个同步工具类,它允许一个或多个线程一 ...

  8. java多线程系类:JUC线程池:03之线程池原理(二)(转)

    概要 在前面一章"Java多线程系列--"JUC线程池"02之 线程池原理(一)"中介绍了线程池的数据结构,本章会通过分析线程池的源码,对线程池进行说明.内容包 ...

  9. java多线程系类:JUC线程池:01之线程池架构

    概要 前面分别介绍了"Java多线程基础"."JUC原子类"和"JUC锁".本章介绍JUC的最后一部分的内容--线程池.内容包括:线程池架构 ...

  10. Java多线程开发技巧

    很多开发者谈到Java多线程开发,仅仅停留在new Thread(...).start()或直接使用Executor框架这个层面,对于线程的管理和控制却不够深入,通过读<Java并发编程实践&g ...

随机推荐

  1. MySQL:互联网公司常用分库分表方案汇总!

    一.数据库瓶颈 不管是IO瓶颈,还是CPU瓶颈,最终都会导致数据库的活跃连接数增加,进而逼近甚至达到数据库可承载活跃连接数的阈值.在业务Service来看就是,可用数据库连接少甚至无连接可用.接下来就 ...

  2. 手撸一个IOC容器

    IoC 什么是IoC? IoC是Inversion of Control(控制反转)的简称,注意它是一个技术思想.描述的是对象创建.管理的事情. 传统开发方式:比如类A依赖类B,往往会在类A里面new ...

  3. pycharm 在flask断点不停止

    For me disabling Gevent compatible option in Preferences > Build, Execution, Deployment has helpe ...

  4. super和this

    super注意点: 1.super调用父类的构造方法,必须在构造方法的第一个 2.super必须只能出现在子类的方法或者构造方法中 3.super和this不能同时调用构造方法 this: 代表的对象 ...

  5. 第六周PTA笔记 括号匹配调整+堆放石子+最大积分+168

    括号匹配调整 如果通过插入" +"和" 1"可以从中得到格式正确的数学表达式,则将带括号的序列称为正确的. 例如,序列 "(())()",& ...

  6. Robot frawork关键字使用报错原因

    对比发现1或者${1}两种方式赋值输出的类型都为整形 >>> ${test1}    set variable   'www' >>> log    ${test1 ...

  7. js数组常用添加方法有两种

        //头部     //this.list.unshift({name:this.itemName,date:new Date()});     //尾部         this.list.p ...

  8. 《重学Java高并发》Sempahore的使用场景与常见误区

    大家好,我是威哥,<RocketMQ技术内幕>一书作者,荣获RocketMQ官方社区优秀布道师.CSDN2020博客执之星Top2等荣誉称号.目前担任中通快递技术平台部资深架构师,主要负责 ...

  9. Qt Creator 源码学习笔记03,大型项目如何管理工程

    阅读本文大概需要 6 分钟 一个项目随着功能开发越来越多,项目必然越来越大,工程管理成本也越来越高,后期维护成本更高.如何更好的组织管理工程,是非常重要的 今天我们来学习下 Qt Creator 是如 ...

  10. 当 dotnet-monitor 遇上 Prometheus, 是种什么样的体验?

    对于开发和运维人员来说, 监控大屏很棒, 让我们来做一个 Dashboard 吧! 大家可能听说过一些 CLI 诊断工具, 比如 dotnet-counters,dotnet-dump 和 dotne ...