-
十一、多线程的应用场景
2012-01-04 16:28:53我们有时貌似熟悉异步、多线程、任务和并行,但有时又不一定特别清楚它们之前的本质区别,甚至在很多复杂的场景下乱用一气。下面我就结合场景来说明在什么情况下该采用什么。同时,还讲解下如何优雅地控制线程,处理...我们有时貌似熟悉异步、多线程、任务和并行,但有时又不一定特别清楚它们之前的本质区别,甚至在很多复杂的场景下乱用一气。下面我就结合场景来说明在什么情况下该采用什么。同时,还讲解下如何优雅地控制线程,处理任务和并行中的异常。
一、在什么场景可以使用多线程
- 想要同时处理多件事:单线程处理不了的,必须使用多线程。(类似于分身术)
- 多个线程分解大任务:用单线程可以做,但是使用多线程可以更快。(类似于左右开弓)
例1:同时处理2件事
场景描述: 执行实际工作20秒钟,20秒时间到结束执行。
public class CalculatePrimes extends Thread{
//使用两个线程,一个用于计时(线程会休眠20秒然后设置一个主线程要检查的标志finished),一个用于执行实际工作。在执行实际工作的线程启动前启动计时线程。//达到20秒钟主线程将停止。
public static final int SECONDS = 20000;
public volatile boolean finished = false;
public void run(){
System.out.println("开始执行实际工作啦");
for (long i = 0l; i < Long.MAX_VALUE; i++) {
if(finished){
break;
}
if(i%100000000==0){
System.out.println("i:"+i);
}
}
System.out.println("结束执行实际工作啦");
}
public static void main(String[] args){
//通过实例化CalculatePrimes类型的对象来创建线程。
CalculatePrimes calculator = new CalculatePrimes();
calculator.start();
try{
System.out.println("执行Thread.sleep前,时间为:"+System.currentTimeMillis());
Thread.sleep(SECONDS);
System.out.println("执行Thread.sleep后,时间为:"+System.currentTimeMillis());
}catch(InterruptedException e){
}
calculator.finished = true;
System.out.println("执行完main()");
}
}执行结果如下:
执行Thread.sleep前,时间为:1325663362824
开始执行实际工作啦
i:0
i:100000000
i:200000000
i:300000000
i:400000000
i:500000000
i:600000000
i:700000000
i:800000000
i:900000000
i:1000000000
i:1100000000
i:1200000000
i:1300000000
执行Thread.sleep后,时间为:1325663382824
结束执行实际工作啦
执行完main()或者用TimerTask来实现:
public class CalculatePrimes extends Thread{
//使用两个线程,一个用于计时(线程会休眠10秒然后设置一个主线程要检查的标志finished),一个用于执行实际工作。在执行实际工作的线程启动之前,启动计时线程。达到10秒钟主线程将停止。
public static final int SECONDS = 20000;
public volatile boolean finished = false;
public void run(){
System.out.println("开始执行实际工作啦");
for (long i = 0l; i < Long.MAX_VALUE; i++) {
if(finished){
break;
}
if(i%100000000==0){
System.out.println("i:"+i);
}
}
System.out.println("结束执行实际工作啦");
}
public static void main(String[] args){
final CalculatePrimes calculator = new CalculatePrimes();
calculator.start();
Timer timer = new Timer();
timer.schedule(
new TimerTask(){
public void run(){
calculator.finished = true;
}
}, SECONDS);
}
}例2:计算密集型的一件事
应用场景:从一个非常大的数组中查找值最大的元素。
public class TenThreads {
private static class WorkerThread extends Thread{
int max = Integer.MIN_VALUE;
int[] ourArray;
public int getMax(){
return max;
}
public WorkerThread(int[] ourArray){
this.ourArray = ourArray;
for (int i = 0; i < ourArray.length; i++) {
System.out.print(ourArray[i]+" ");
}
System.out.println();
}
public void run(){
for (int i = 0; i < ourArray.length; i++) {
max = Math.max(max,ourArray[i]);
}
}
}
public static void main(String[] args){
WorkerThread[] threads = new WorkerThread[9];
int[][] bigMatrix = {{1,10,100},{2,20,200},{3,33,333},{4,40,444},{5,50,500},{6,66,666},{7,77,777},{8,88,888},{9,99,999}};
int max = Integer.MIN_VALUE;
for (int i = 0; i < 9; i++) {
threads[i] = new WorkerThread(bigMatrix[i]);
threads[i].start();
}
try{
for (int i = 0; i < 9; i++) {
threads[i].join();
max = Math.max(max,threads[i].getMax());
}
}catch(InterruptedException e){
}
System.out.println("Maximum value is:"+max);
}
}执行结果:
1 10 100
2 20 200
3 33 333
4 40 444
5 50 500
6 66 666
7 77 777
8 88 888
9 99 999
Maximum value is:999当然这个示例程序的数据量不大,只起到示例说明的作用,只要大家明白意图就好,大任务在实际工作中还是会遇到比较多的。
二、区分异步和多线程应用场景
举个场景--要获取某个网页的内容并显示出来。来看下应该选择异步还是多线程更适合。
可以预见,如果该网页的内容很多,或者当前的网络状况不太好,获取网页的过程会持续较长时间。
于是,我们可能会想到用 新起工作线程的方法来完成这项工作,这样在等待网页内容返回的过程中界面就不会被阻滞了。是的,上面的程序解决了界面阻滞的问题,但是,它高效吗?答案是:不。因为这样是为了获取网页,新起了一个工作线程,然后在读取网页的整个过程中,该工作线程始终被阻滞,直到获取网页完毕为止。在整个过程中,工作线程被占用着,这意味着系统的资源始终被消耗着、等待着。
如果使用异步模式去实现,它使用线程池进行管理。新起异步操作后,会将工作丢给线程池中的某个工作线程来完成。当开始I/O操作的时候,异步会将工作线程还给线程池,这意味着获取网页的工作不会再占用任何CPU资源了。直到异步完成,即获取网页完毕,异步才会通过回调的方式通知线程池。可见,异步模式借助于线程池,极大地节约了CPU的资源。
注:直接内存访问,是一种不经过CPU而直接进行内存数据存储的数据交换模式。通过直接内存访问的数据交换几乎可以不损耗CPU的资源。在硬件中,硬盘、网卡、声卡、显卡等都有D直接内存访问功能。异步编程模型就是让我们充分利用硬件的直接内存访问功能来释放CPU的压力。
明白了异步和多线程的区别后,我们来总结下两者的应用场景:
- 计算密集型工作,采用多线程。
- IO密集型工作,采用异步机制。
三、线程在现有其他技术中的应用
在使用下面这些技术是,我们必须始终假设可以在多个线程中并发地执行的情境,这就要求我们必须适当同步共享数据。
- Servlet和JSP技术
Servlet容器创建多个线程,在这些线程中执行servlet请求。在你写servlet程序时,你不需要知道你的servlet请求是在什么线程中执行的。但是你要知道,如果同时有多个对相同URL的请求入栈,那么同一个servlet可能会同时在多个线程中是活动的。这要求我们再编写servlet或jsp时,必须始终假设可以在多个线程中并发地执行同一个servlet或jsp的情境,这就要求我们必须适当同步servlet或jsp文件访问的任何共享数据,包括servlet对象本身的字段。
2. 现实RMI对象
RMI让你可以调用在其他JVM中运行的对象并对其进行操作。当调用远程方法时,RMI编译器创建的RMI存根会打包方法参数,并通过网络警它们发送到远程系统,然后远程系统会将它们解包并调用远程方法。
假设你创建了一个RMI对象,并将它注册到RMI注册表或者JNDI名称空间(Java Naming and Directory Interface即Java命名和目录接口)。当远程客户机调用其中的一个方法是,该方法会在什么线程中执行呢?假设RMI对象的常用方法是继承UnicastRemoteObject,在构造UnicastRemoteObject时,会初始化用于分派远程方法调用的基础结构,这包括用于接收远程调用请求的套接字侦听器和一个或多个执行远程请求的线程。所以,当接收到执行RMI方法的请求时,这些方法将在RMI管理的线程中执行。
3.Collection集合类
Collection集合类在自身实现时并没有使用同步,这就意味着程序员在使用集合类时,在多线程的情境下如果没有进行同步,是有可能出问题的,即不能在不进行同步的情况下在多线程场景下使用集合类。通过每次访问共享集合中的方法时使用同步,可以在多线程应用程序中使用Collection类。对于任何给定的集合,每次必须用同一个锁进行同步。通常可以选择集合对象本身作为锁。另一种方法是使用Collections类提供的一组List、Map、Set的封装器。如可以用Collections.synchronizedMap封装Map,它将确保所有对该映射的访问都被正确同步了。
-
iOS 多线程在项目中的应用场景(一)
2017-07-20 16:32:50获取线上版本号是一件很耗时的操作,所以开辟一个... //说明:开辟子线程运行耗时代码块,然后在主线程中刷新和显示 dispatch_async(dispatch_get_global_queue(0, 0), ^{ //检测新版本 self.hasNewVersion = [se获取线上版本号是一件很耗时的操作,所以开辟一个子线程,代码如下
//检测新版本 //说明:开辟子线程运行耗时代码块,然后在主线程中刷新和显示 dispatch_async(dispatch_get_global_queue(0, 0), ^{ //检测新版本 self.hasNewVersion = [self checkVersion]; [self.tableView reloadData]; });
此处加了一个 hasVersion 标识,它控制着版本升级提示的红点显示。中间遇到的一个问题就是红点该显示的没有显示出来,或是很久才显示出来,对tableView上的cell做点击操作,第一次点击不会响应,点击或者滑动tableView红点立刻就显示出来了,显然,红点显示没有放在主线程中进行,当滑动tableView时主线程刷新红点才出来,于是把上面代码中tableView的刷新加在了主线程
dispatch_async(dispatch_get_global_queue(0, 0), ^{ //检测新版本 self.hasNewVersion = [self checkVersion]; dispatch_async(dispatch_get_main_queue(), ^{ [self.tableView reloadData]; }); });
这样红点提示就顺利显示出来了。原来之前的刷新房子啊了子线程中。注意到,放在子线程中刷新UI,为什么会出现红点有时会出现有时不会呢?带着这个问题查阅了网上的资料,其中http://blog.sina.com.cn/s/blog_45e2b66c0102v254.html这篇文章给了我很大启发,子线程中对所有其他UI更新都要等到该子线程生命周期结束才进行,如果子线程一直在运行,则子线程中的UI更新的函数栈主线程无法获知,即无法更新。
//MARK:查看新版本 - (BOOL)checkVersion { BOOL hasNewVersion = NO; NSString *newVersion; NSURL *url = [NSURL URLWithString:@"http://itunes.apple.com/cn/lookup?id=xxxxxxx"]; NSString *jsonResponseString = [NSString stringWithContentsOfURL:url encoding:NSUTF8StringEncoding error:nil]; if (jsonResponseString != nil) { NSData *data = [jsonResponseString dataUsingEncoding:NSUTF8StringEncoding]; //解析json数据 id json = [NSJSONSerialization JSONObjectWithData:data options:NSJSONReadingMutableContainers error:nil]; NSArray *array = json[@"results"]; for (NSDictionary *dic in array) { newVersion = [dic valueForKey:@"version"]; } Log(@"通过appStore获取的版本号是:%@",newVersion); //获取本地软件的版本号 NSString *localVersion = [[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleShortVersionString"]; //CFBundleVersion--获取当前build版本 CFBundleShortVersionString--获取当前app版本 [[NSUserDefaults standardUserDefaults] setBool:NO forKey:@"findNewVersion"]; //对比发现的新版本和本地版本 NSComparisonResult comparisonResult = [localVersion compare:newVersion options:NSNumericSearch]; switch (comparisonResult) { case NSOrderedSame: Log(@"本地版本与线上版本相同不需要更新"); hasNewVersion = NO; break; case NSOrderedAscending: Log(@"本地版本 < 线上版本,需要更新"); [[NSUserDefaults standardUserDefaults] setBool:YES forKey:@"findNewVersion"]; hasNewVersion = YES; break; case NSOrderedDescending: Log(@"本地版本 > 线上版本,不需要更新"); hasNewVersion = NO; break; default: break; } } return hasNewVersion; };
-
操作系统------多线程与多进程的区别和应用场景
2017-08-28 11:30:12通过表的归纳和总结说明: ...这种原则最常见的应用就是Web服务器了,来一个连接建立一个线程,断了就销毁线程,要是用进程,创建和销毁的代价是很难承受的 2)需要进行大量计算的优先使用线程(CPU频繁切换) 所通过表的归纳和总结说明:
1)需要频繁创建销毁的优先用线程(进程的创建和销毁开销过大)
原因请看上面的对比。
这种原则最常见的应用就是Web服务器了,来一个连接建立一个线程,断了就销毁线程,要是用进程,创建和销毁的代价是很难承受的
2)需要进行大量计算的优先使用线程(CPU频繁切换)
所谓大量计算,当然就是要耗费很多CPU,切换频繁了,这种情况下线程是最合适的。
这种原则最常见的是图像处理、算法处理。
3)强相关的处理用线程,弱相关的处理用进程
什么叫强相关、弱相关?理论上很难定义,给个简单的例子就明白了。
一般的Server需要完成如下任务:消息收发、消息处理。“消息收发”和“消息处理”就是弱相关的任务,而“消息处理”里面可能又分为“消息解码”、“业务处理”,这两个任务相对来说相关性就要强多了。因此“消息收发”和“消息处理”可以分进程设计,“消息解码”、“业务处理”可以分线程设计。
当然这种划分方式不是一成不变的,也可以根据实际情况进行调整。
4)可能要扩展到多机分布的用进程,多核分布的用线程
原因请看上面对比。 -
2020-10-17:volatile有什么用?能否用一句话说明下volatile的应用场景?
2020-10-18 22:58:06能否用一句话说明下volatile的应用场景? 前言volatile有什么用?能否用一句话说明下volatile的应用场景? 前言 每日一题专栏 volatile有什么用?能否用一句话说明下volatile的应用场景? volatile保证内存可见性... -
Java 多线程在交易中间件测试中的应用
2014-06-03 16:17:46以 IMS Connect V13 组件的自动化测试案例验证说明 <!-- LEADSPACE_BODY_END -->&...-- SUMMARY_BEGIN --&...Java 的多线程编程能有效地模拟这些场景,使测试场景丰富,测试效果较好。本文以... -
多线程-线程之间的通信
2019-03-23 09:24:02我们经常说线程之间的通信,线程之间的协作,但是根据他的应用场景其实是很容易搞清楚的; 比如说:生产者-消费者,又比如说队列,等等的Demo,在根据生活场景来说: 去菜市场买水果,你是消费者,菜市场是生产者,你没有钱,... -
44. 举例说明 sychronized 修饰符应用的场景
2020-10-02 10:42:18synchronized 关键字是JAVA中内置的语言级同步原语,可以通过使用这个关键字实现多线程间访问之间的同步。synchronized 关键字可以作为函数的修饰符,也可以直接在函数语句中使用,也就是平时说的同步方法和同步语句... -
python多线程
2019-04-10 10:29:50首先,说明一下多线程的应用场景:当python处理多个任务时,这些任务本质是异步的,需要有多个并发事务,各个事务的运行顺序可以是不确定的、随机的、不可预测的。计算密集型的任务可以顺序执行分隔成的多个子任务,... -
java多线程等待协调工作:CountDownLatch类的高级应用
2016-07-06 10:58:00基本上对于线程初步了解的人,都是使用synchronized来同步线程的,也确实,它也是可以满足一些常用的问题。那么我们来说一些它不能解决的问题(其实是不怎么好解决的问题,并不是真的不能解决) 1.1:场景一 问题:... -
多线程的使用
2013-11-02 13:09:04IOS多线程编程1:首先简单介绍什么叫线程可并发执行的,拥有最小系统资源,共享进程资源的基本调度单位。共用堆,自有栈(官方资料说明iOS主线程栈大小为1M,其它线程为512K)。并发执行进度不可控,对非原子操作易... -
saas自定义字段_在构建SaaS应用程序时定义多线程阈值
2020-06-28 00:03:58存档日期:2019年5月14日 | 首次发布:2013年4月18日 ... 作者说明了在多线程SaaS故障场景中应采取的积极措施。 此内容不再被更新或维护。 全文以PDF格式“按原样”提供。 随着技术的飞速发展,某些内容,步骤或... -
多线程应用(一)—Http请求阻塞回调处理
2017-11-13 16:48:28多线程应用(一)—Http请求阻塞回调处理1.需求描述1.1场景说明:由于,微信端的业务需求量越来越大.将业务与微信第三方事件处理耦合在一起的单一项目的结构已经逐渐暴露出,承载能力不足的缺点.所以,需要将与微信的交互... -
多线程基础
2020-05-18 21:59:29多线程基础 线程是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。...线程特性和应用场景 特性 并行 异步 应用 网络请求分发(Tomcat处理Socket就是通过线程来实现的) -
java多线程-你需要了解的一切
2019-05-30 13:18:54java多线程详解线程 - 介绍定义作用状态说明线程分类守护线程守护线程 与 非守护线程的区别线程优先级表示设置多线程 - 介绍定义作用应用场景实现方式线程调度-调度方式线程调度 - 调度优先级优先级调度思想:线程... -
Reactor 模式(单 Reactor 单线程、单 Reactor 多线程、主从 Reactor 多线程)
2020-05-17 11:59:18Reactor 单线程工作原理示意图方案说明方案优缺点分析优点缺点使用场景单 Reactor 多线程工作原理示意图方案说明方案优缺点分析优点缺点主从 Reactor 多线程工作原理示意图方案说明方案优缺点分析优点缺点结合实例... -
多线程应用--Http请求阻塞回调处理
2018-09-05 05:38:271.1场景说明: 由于,微信端的业务需求量越来越大.将业务与微信第三方事件处理耦合在一起的单一项目的结构已经逐渐暴露出,承载能力不足的缺点.所以,需要将与微信的交互从业务逻辑中分离出,单独进行管理和处理. 这样做... -
redis应用场景
2020-12-23 16:06:42说明 Redis在很多方面与其他数据库解决方案不同:它使用内存提供主存储支持,而仅使用硬盘做...另外在一些需要大容量数据集的应用,Redis也并不适合,因为它的数据集不会超过系统可用的内存。所以如果你有大数据应用 -
多线程join分析
2020-02-09 23:56:26多线程下join的应用与分析 一、场景:多线程环境下 二、需求:假如有两个线程,如何保证线程的顺序执行 三、解决方案:使用join的方式 四、原理分析 因此就引入了join的使用,当然保证线程的顺序执行肯定不止join这... -
Python--多线程
2016-01-28 22:57:00首先,说明一下多线程的应用场景:当python处理多个任务时,这些任务本质是异步的,需要有多个并发事务,各个事务的运行顺序可以是不确定的、随机的、不可预测的。计算密集型的任务可以顺序执行分隔成的多个子任务,... -
Java多线程专题-线程锁深度解析
2019-06-05 21:37:19我们在前面章节也提到过多线程的锁机制,但没有深入的去研究锁的种类以及其用法。在这里做一个深度说明。...下面会讲到这两种概念锁的具体实现细节何其应用场景。 悲观锁(Pessimistic Lock) 顾... -
多线程工具之CompletionService
2018-10-19 11:03:00具体说说CompletionService的应用场景和使用方法。 比如我们有10个线程需要丢到线程池里面去执行,然后把10个线程的执行结果返回回来处理。如果没有使用CompletionService,我们的实现方式如下。首先创建线程类... -
CountDownLatch应用场景实践
2020-07-02 14:33:47CountDownLatch是一个并发工具类,作用是允许一个或多个线程等待其他线程完成操作。我们有时会称之为发令枪。有一个个形象的例子能说明其功能:公司召开一个全体股东会,需要所有股东到场了才能正式开始。会议室... -
【多线程 5】线程池的类型以及submit()和execute()的区别
2016-10-28 21:24:00就跟题目说的一样,本篇博客,本宝宝主要介绍两个方面的内容,其一:线程池的类型及其...并不是涉及到启用多线程的地方,就非得整出个线程池出来! 1.1,什么是线程池 线程池是一种多线程处理形式,处理过程中将... -
多线程(或进程)同时退出时判断是哪些线程退出”的实例代码
2016-11-09 12:04:41工程是使用vs2008编译的,代码中带有详细说明,已经将多余的部分删除,只留有可以编译的源代码,由于提供的只是一种方法,所以示例代码比较精简,你可以使用用于其它应用场景。如有问题或者一些改进意见,麻烦请评论... -
iOS多线程开发
2015-01-05 23:25:051:首先简单介绍什么叫线程可并发执行的,拥有最小系统资源,共享进程资源的基本调度单位。共用堆,自有栈(官方资料说明iOS主线程栈大小为1M,其它线程为512K)。...应用场景:逻辑执行时间过长,严重影响交 -
IOS多线程编程
2014-02-26 17:09:501:首先简单介绍什么叫线程 可并发执行的,拥有最小系统资源,共享进程资源的基本调度单位。共用堆,自有栈(官方资料说明iOS主线程栈大小为1M,其它线程为512K)。并发执行进度不可控,对非原子...应用场景:逻辑 -
并发编程应用场景_并发编程之CountDownLatch原理与应用
2020-12-26 08:33:12类作用说明Semaphore信号量,可以通过控制“许可证”的数量,来保证线程之间的配合线程只有拿到“许可证”后才能继续运行,相比于其它的同步器,更灵活CyclicBarrier线程会等待,直到足够多线程达到...
-
Windows系统管理
-
基于UDP协议的聊天程序 MFC环境下的
-
培训效果评价方法.pdf
-
修复非平面混合无线传感器网络中的覆盖Kong
-
MaxScale 实现 MySQL 读写分离与负载均衡
-
响应式编程入门与实战(Reactor、WebFlux、R2DBC)
-
龙芯生态应用开发基础:C语言精要
-
2016年下半年 信息安全工程师 上午试卷 综合知识 软考真题【含答案和答案解析】
-
工程制图 AutoCAD 2012 从二维到三维
-
项目经理成长之路
-
代管-源码
-
Ubuntu 系统安装 - VirtualBox 版
-
2013年上半年 信息处理技术员 上午试卷 综合知识 软考真题【含答案和答案解析】
-
Unsupported major.minor version 52.0
-
2012年下半年 信息处理技术员 上午试卷 综合知识 软考真题【含答案和答案解析】
-
2018年下半年 信息系统运行管理员 上午试卷 综合知识 软考真题【含答案和答案解析】
-
【Python-随到随学】 FLask第一周
-
谈谈对面向对象思想的理解
-
为什么说要搞定微服务架构,先搞定RPC框架?
-
【Python-随到随学】FLask第二周