java多线程并发编程
Executor框架
executor.execute(task2);
scheduler.scheduleAtFixedRate(task2, 10, 10, TimeUnit.SECONDS);
单独使用Runnable时:
无法获得返回值
单独使用Callable时:
无法在新线程中(new Thread(Runnable r))使用,只能使用ExecutorService
Thread类只支持Runnable
FutureTask:
实现了Runnable和Future,所以兼顾两者优点
既可以使用ExecutorService,也可以使用Thread
- Callable pAccount = new PrivateAccount();
- FutureTask futureTask = new FutureTask(pAccount);
- // 使用futureTask创建一个线程
- Thread thread = new Thread(futureTask);
- thread.start();
=================================================================
public interface Future<V> Future 表示异步计算的结果。
Future有个get方法而获取结果只有在计算完成时获取,否则会一直阻塞直到任务转入完成状态,然后会返回结果或者抛出异常。
FutureTask是为了弥补Thread的不足而设计的,它可以让程序员准确地知道线程什么时候执行完成并获得到线程执行完成后返回的结果(如果有需要)。
FutureTask是一种可以取消的异步的计算任务。它的计算是通过Callable实现的,它等价于可以携带结果的Runnable,并且有三个状态:等待、运行和完成。完成包括所有计算以任意的方式结束,包括正常结束、取消和异常。
Future 主要定义了5个方法:
1) boolean cancel(boolean mayInterruptIfRunning):试图取消对此任务的执行。如果任务已完成、或已取消,或者由于某些其他原因而无法取消,则此尝试将失败。当调用 cancel 时,如果调用成功,而此任务尚未启动,则此任务将永不运行。如果任务已经启动,则 mayInterruptIfRunning 参数确定是否应该以试图停止任务的方式来中断执行此任务的线程。此方法返回后,对 isDone() 的后续调用将始终返回 true。如果此方法返回 true,则对 isCancelled() 的后续调用将始终返回 true。 (取消当前任务或终止线程)
2)boolean isCancelled():如果在任务正常完成前将其取消,则返回 true。
3)boolean isDone():如果任务已完成,则返回 true。 可能由于正常终止、异常或取消而完成,在所有这些情况中,此方法都将返回 true。
4)V get()throws InterruptedException,ExecutionException:如有必要,等待计算完成,然后获取其结果(futuretask获取Callable返回的结果)。
5)V get(long timeout,TimeUnit unit) throws InterruptedException,ExecutionException,TimeoutException:如有必要,最多等待为使计算完成所给定的时间之后,获取其结果(如果结果可用)。
- public class FutureTask<V> extends Object
- implements Future<V>, Runnable
FutureTask类是Future 的一个实现,并实现了Runnable,所以可通过Excutor(线程池) 来执行,也可传递给Thread对象执行。如果在主线程中需要执行比较耗时的操作时,但又不想阻塞主线程时,可以把这些作业交给Future对象在后台完成,当主线程将来需要时,就可以通过Future对象获得后台作业的计算结果或者执行状态。
Executor框架利用FutureTask来完成异步任务,并可以用来进行任何潜在的耗时的计算。一般FutureTask多用于耗时的计算,主线程可以在完成自己的任务后,再去获取结果。
}
}
calc.close();
- public interface ExecutorService extends Executor {
- void shutdown();
- List<Runnable> shutdownNow();
- boolean isShutdown();
- boolean isTerminated();
- boolean awaitTermination(long timeout, TimeUnit unit)
- throws InterruptedException;
- }
本文,我们逐一分析里面的每个方法。
首先,我们需要创建一个任务代码,这段任务代码主要是随机生成含有10个字符的字符串
- /**
- * 随机生成10个字符的字符串
- * @author dream-victor
- *
- */
- public class Task1 implements Callable<String> {
- @Override
- public String call() throws Exception {
- String base = "abcdefghijklmnopqrstuvwxyz0123456789";
- Random random = new Random();
- StringBuffer sb = new StringBuffer();
- for (int i = 0; i < 10; i++) {
- int number = random.nextInt(base.length());
- sb.append(base.charAt(number));
- }
- return sb.toString();
- }
- }
然后,我们还需要一个长任务,这里我们默认是沉睡10秒,
- /**
- * 长时间任务
- *
- * @author dream-victor
- *
- */
- public class LongTask implements Callable<String> {
- @Override
- public String call() throws Exception {
- TimeUnit.SECONDS.sleep(10);
- return "success";
- }
- }
OK,所有前期准备完毕,下面我们就来分析一下ExecutorService接口中和生命周期有关的这些方法:
1、shutdown方法:这个方法会平滑地关闭ExecutorService,当我们调用这个方法时,ExecutorService停止接受任何新的任务且等待已经提交的任务执行完成(已经提交的任务会分两类:一类是已经在执行的,另一类是还没有开始执行的),当所有已经提交的任务执行完毕后将会关闭ExecutorService。这里我们先不举例在下面举例。
2、awaitTermination方法:这个方法有两个参数,一个是timeout即超时时间,另一个是unit即时间单位。这个方法会使线程等待timeout时长,当超过timeout时间后,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用。例如:
- ExecutorService service = Executors.newFixedThreadPool(4);
- service.submit(new Task1());
- service.submit(new Task1());
- service.submit(new LongTask());
- service.submit(new Task1());
- service.shutdown();
- while (!service.awaitTermination(1, TimeUnit.SECONDS)) {
- System.out.println("线程池没有关闭");
- }
- System.out.println("线程池已经关闭");
这段代码中,我们在第三次提交了一个长任务,这个任务将执行10秒沉睡,紧跟着执行了一次shutdown()方法,假设:这时ExecutorService被立即关闭,下面调用service.awaitTermination(1, TimeUnit.SECONDS)方法时应该返回true,程序执行结果应该只会打印出:“线程池已经关闭”。但是,真实的运行结果如下:
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池没有关闭
- 线程池已经关闭
这说明我们假设错误,service.awaitTermination(1, TimeUnit.SECONDS)每隔一秒监测一次ExecutorService的关闭情况,而长任务正好需要执行10秒,因此会在前9秒监测时ExecutorService为未关闭状态,而在第10秒时已经关闭,因此第10秒时输出:线程池已经关闭。这也验证了shutdown方法关闭ExecutorService的条件。
3、shutdownNow方法:这个方法会强制关闭ExecutorService,它将取消所有运行中的任务和在工作队列中等待的任务,这个方法返回一个List列表,列表中返回的是等待在工作队列中的任务。例如:
- ExecutorService service = Executors.newFixedThreadPool(3);
- service.submit(new LongTask());
- service.submit(new LongTask());
- service.submit(new LongTask());
- service.submit(new LongTask());
- service.submit(new LongTask());
- List<Runnable> runnables = service.shutdownNow();
- System.out.println(runnables.size());
- while (!service.awaitTermination(1, TimeUnit.MILLISECONDS)) {
- System.out.println("线程池没有关闭");
- }
- System.out.println("线程池已经关闭");
这段代码中,我们限制了线程池的长度是3,提交了5个任务,这样将有两个任务在工作队列中等待,当我们执行shutdownNow方法时,ExecutorService被立刻关闭,所以在service.awaitTermination(1, TimeUnit.MILLISECONDS)方法校验时返回的是false,因此没有输出:线程池没有关闭。而在调用shutdownNow方法时,我们接受到了一个List,这里包含的是在工作队列中等待执行的任务,由于线程池长度为3,且执行的都是长任务,所以当提交了三个任务后线程池已经满了,剩下的两次提交只能在工作队列中等待,因此我们看到runnables的大小为2,结果如下:
- 2
- 线程池已经关闭
4、isTerminated方法:这个方法会校验ExecutorService当前的状态是否为“TERMINATED”即关闭状态,当为“TERMINATED”时返回true否则返回false。例如:
- ExecutorService service = Executors.newFixedThreadPool(3);
- service.submit(new Task1());
- service.submit(new Task1());
- service.submit(new LongTask());
- service.shutdown();
- System.out.println(System.currentTimeMillis());
- while (!service.isTerminated()) {
- }
- System.out.println(System.currentTimeMillis());
这段代码我们执行了两个正常的任务和一个长任务,然后调用了shutdown方法,我们知道调用shutdown方法并不会立即关闭ExecutorService,这时我们记录一下监测循环执行前的时间,在没有关闭前我们一直进入一个空循环中,直到 ExecutorService关闭后退出循环,这里我们知道长任务执行时间大约为10秒,我们看一下上述程序运行结果:
- 1303298818621
- 1303298828634
- 相差:10013毫秒,转换一下除以1000,得到相差大约10秒
这10秒正好是长任务执行的时间,因此在 ExecutorService正常关闭后isTerminated方法返回true。
5、isShutdown方法:这个方法在ExecutorService关闭后返回true,否则返回false。方法比较简单不再举例。
以上讨论是基于ThreadPoolExecutor的实现,不同的实现会有所不同需注意。
java多线程并发编程的更多相关文章
- Java 多线程并发编程一览笔录
Java 多线程并发编程一览笔录 知识体系图: 1.线程是什么? 线程是进程中独立运行的子任务. 2.创建线程的方式 方式一:将类声明为 Thread 的子类.该子类应重写 Thread 类的 run ...
- 【收藏】Java多线程/并发编程大合集
(一).[Java并发编程]并发编程大合集-兰亭风雨 [Java并发编程]实现多线程的两种方法 [Java并发编程]线程的中断 [Java并发编程]正确挂起.恢复.终止线程 [ ...
- java多线程并发编程与CPU时钟分配小议
我们先来研究下JAVA的多线程的并发编程和CPU时钟振荡的关系吧 老规矩,先科普 我们的操作系统在DOS以前都是单任务的 什么是单任务呢?就是一次只能做一件事 你复制文件的时候,就不能重命名了 那么现 ...
- Java基础系列篇:JAVA多线程 并发编程
一:为什么要用多线程: 我相信所有的东西都是以实际使用价值而去学习的,没有实际价值的学习,学了没用,没用就不会学的好. 多线程也是一样,以前学习java并没有觉得多线程有多了不起,不用多线程我一样可以 ...
- Java 多线程并发编程
导读 创作不易,禁止转载! 并发编程简介 发展历程 早起计算机,从头到尾执行一个程序,这样就严重造成资源的浪费.然后操作系统就出现了,计算机能运行多个程序,不同的程序在不同的单独的进程中运行,一个进程 ...
- Java多线程并发编程/锁的理解
一.前言 最近项目遇到多线程并发的情景(并发抢单&恢复库存并行),代码在正常情况下运行没有什么问题,在高并发压测下会出现:库存超发/总库存与sku库存对不上等各种问题. 在运用了 限流/加锁等 ...
- Java多线程并发编程一览笔录
线程是什么? 线程是进程中独立运行的子任务. 创建线程的方式 方式一:将类声明为 Thread 的子类.该子类应重写 Thread 类的 run 方法 方式二:声明实现 Runnable 接口的类.该 ...
- Java 多线程并发编程面试笔录一览
知识体系图: 1.线程是什么? 线程是进程中独立运行的子任务. 2.创建线程的方式 方式一:将类声明为 Thread 的子类.该子类应重写 Thread 类的 run 方法 方式二:声明实现 Runn ...
- java多线程 并发 编程
转自:http://www.cnblogs.com/luxiaoxun/p/3870265.html 一.多线程的优缺点 多线程的优点: 1)资源利用率更好 2)程序设计在某些情况下更简单 3)程序响 ...
随机推荐
- MAC上的包管理利器
Homebrew- MAC上的包管理利器 2013-07-01 16:25 by 黄博文, 76 阅读, 0 评论, 收藏, 编辑 包管理器是神马东西?让我们看看wikipedia上的介绍. In s ...
- MapXtreme+Asp.net 动态轨迹(请求大神指点)
功能简介:在MapXtreme+Asp.net的环境下实现轨迹回放功能,经过两天的努力基本实现此功能.但还有部分问题需要解决,求大神们指点迷津,问题会在结尾处提出. 客户端前台页面 <asp:S ...
- magento 小问题解决方案集
magento错误 Mage registry key "_singleton/core/resource" already exists解决方法:1.清理magento的var/ ...
- 深入浅出Mybatis-sql自动生成
本文提供了一种自动生成sql语句的方法,它针对的对象是有主键或唯一索引的单表,提供的操作有增.删.改.查4种.理解本文和本文的提供的代码需要有java注解的知识,因为本文是基于注解生成sql的.本文适 ...
- Ubuntu12.10 下搭建基于KVM-QEMU的虚拟机环境(十五)
接下来我们试验虚拟机的网络配置功能,KVM虚拟机网络配置主要有两种方式:NAT方式和Bridge方式.今天我们主要理解和实验NAT方式的网络配置. NAT方式是kvm安装后的默认方式,它支持主机与虚拟 ...
- EF框架操作postgresql,实现WKT类型坐标的插入,查询,以及判断是否相交
1.组件配置 首先,要下载.NET for Postgresql的驱动,npgsql,EF6,以及EntityFramework6.Npgsql,版本号 3.1.1.0. 由于是mvc项目,所以,把相 ...
- Js-Html 前端系列--页面撑开头尾
今天学习过程中,发现一个超实用的方法,就是当页面有尾部,但是内容又不多的情况下,让中间的内容把尾部撑到底部. <script type="text/javascript"&g ...
- 将List 中的ConvertAll的使用:List 中的元素转换,List模型转换, list模型转数组
一,直接入代码 using System; using System.Collections.Generic; using System.Linq; using System.Web; using S ...
- 基于Unity的Profiler性能分析
A. WaitForTargetFPS: Vsync(垂直同步)功能所,即显示当前帧的CPU等待时间 B. Overhead: Profiler总体时间-所有单项的记录时 ...
- Ubuntu中Qt新建窗体提示lGL错误
提示错误: cannot find -lGL collect2:error:ld returned 1 exit status 这是因为系统缺少链接库,终端输入: sudo apt-get insta ...