最近做项目,高并发的情况比较常见,因此常常需要用到多线程。而之前一直对多线程处于一个比较模糊的状态,这次终于清晰了点儿。其实理解多线程可以和异步调用结合起来理解会比较好。
对于同步调用和异步调用,可以用以下伪代码来粗略的看一下:
同步调用:
public void test() { //某段代码 //这里是入db的操作 this.saveDataToDb(Map params); }
异步调用:
public void test() { //某段代码 //这里是入db的操作 new Thread(new SaveDataToDb(Map param)).start(); }
其中SaveDataToDb类是一个实现了Runnable接口的线程。
在同步调用中,你必须等待“某段代码”执行完毕后,再执行saveDataToDb操作。而入db是一个相对耗时的操作。在非并发的情形下,这样做性能上的劣势还可能不能体现出来。但当是高并发的情况和不定时被执行的情况下呢?我们总不能执行到saveDataToDb处时,就一直等待saveDataToDb执行完,才开始接收下一次的任务请求吧!所以就必须开启另一个线程。
于是就用到了异步调用。把saveDataToDb的逻辑放到一个线程类的run方法里,而当执行到入db的操作处时,我们就启用该线程来“异步”地执行入db的操作。这样,在高并发的情况下,“下一次”的请求就可以不用等到“这一次”入db的操作执行完毕后再执行了。
具体到实际情景就是:如果执行某项任务需要甲乙协作完成,甲做完“下一步”给乙做,而乙做的东西又比较耗时,则此次任务必须是甲等待乙完成之后再接收下一次该任务。其中的等待时间就浪费了。如果是异步的话,则甲完成他的部分,可以不必等待乙完成再接收下一次任务。甲每次完成就把他的结果部分丢给乙,而乙是可以不必跟甲在同一流程里执行的。这样就达到了“错开”的效果。也节省了时间。
当然,我们更为合理的做法是用一个线程池去控制多线程,线程池可以负责多个线程的创建、等待、调度和销毁等。因为如果在高并发的情况下,你每到入db处,就new一个线程,当new的线程越来越多时,会造成内存的大量占用。
拿甲乙的例子来说,线程池就是负责创建了很多个“乙”,他们共同协作完成比较耗时的入db的操作。效率上因此更加高效了。线程池此时可以理解成“包工头”,当一个“乙”做完之后,就可以接着做了,而当一个“乙”正在做,则不会把任务分配给他,只有当他做完之后,才会再次分配入db的任务给他。
我们不妨在web项目初始化的时候,就创建一个线程池的类,这个线程池类定义好了一些多线程的配置然后定义一个调度线程的静态方法。我们需要用线程池调用线程时,就调用该静态方法即可。
需要注意的是,“异步”调用受线程影响很大。就拿上述的入db操作的线程来说,可能在高并发的情形下,线程未能及时被线程池回收而不够用,入db的操作就会被down掉或不能及时入db。所以需要我们好好控制线程池的配置。
【2016-1-18相关补充】
观察下面的代码:
public static void main(String[] args) { new Thread(new Runnable() { @Override public void run() { System.out.println("模拟订阅"); } }).start(); new Thread(new Runnable() { @Override public void run() { while(true) { try { Thread.sleep(3000); System.out.println("每隔3s do something。。。========"); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); // new Thread(new Runnable() { // @Override // public void run() { while(true) { try { Thread.sleep(6000); System.out.println("每隔6s do otherthing。。。"); } catch (InterruptedException e) { e.printStackTrace(); } } // } // }).start(); }
运行该main程序之后,你会发现,虽然第二步用的是一个while循环,但是它是写在一个内部的匿名线程类里面,你会发现还是会重复 打印 [每隔3s do something。。。========]2次之后再打印一次 [每隔6s do otherthing。。。]的情况;程序并没有在第二步被while循环阻塞。
但是如果你第二步不是写在匿名线程里,则编译都会报错,因为第三步根本就变成unreachable的。
注意,上述代码的写法中,第三步的whille循环可用线程套起来也可以不用,但注意一定要sleep , 否则程序会永远阻塞在第三步。第二步就不会每隔一段时间去执行啦。(其实第二步如果你不sleep第三步也是执行不到的,所以无论哪个while都应该sleep)
上面的写法其实之前我也不是很熟悉,但今天碰到了顺便学习了一下。这是非常实用的。比如一个程序启动往往有每隔段时间去检查队列的需求,这时候其实你并不需要劳师动众去专门写一个定时任务(因为此时逻辑很简单哒)。巧妙应用while是完全能达到效果滴。
综上所述,线程其实也是异步的一个很重要的反映。
相关推荐
Java多线程实现异步调用实例。运行Main可以看到结果。main是主线程,另有A,B,C三个线程用不同的时间跑完。
labview2015关于异步调用功能的研究工程,主要用于解决主线程负责快速轮询,多个子线程负责具体的任务处理并反馈给主线程(子线程需要长时间处理任务),解决单纯的可重复VI不能实现并行的功能。
多线程异步调用(并参递参数)经典代码示例
Unity异步线程调用主线程脚本程序,在Unity中异步线程调用主线程会报错,所以编写了一个Loom
多线程和异步操作两者都可以达到避免调用线程阻塞的目的,从而提高软件的可响应性。甚至有些时候我们就认为多线程和异步操作是等同的概念。但是,多线程和异步操作还是有一些区别的。而这些区别造成了使用多线程和...
假设了一个场景,用户在局域网Web服务器上下了一个单,程序将这个订单发送到远程服务器,然后用户付款到远程服务器, ...有可能用户下单之后就把本网站的下单页面给关闭...关键字:C# ,Asp.Net, MVC, TASK异步调用测试!
这本书详细介绍了多线程异步操作,适合初学者和想深入了解的员工
wcf多线程和异步操作 异步服务的调用 异步服务的实现 读取文件demo
AsyncCalls(异步调用函数),Delphi异步调用函数的单元和例子
本文给大家分享java多线程实现异步调用的方法,感兴趣的朋友跟着脚本之家小编一起学习吧
使用python多线程异步提高模型部署到rk3588NPU使用率_python源码+项目使用说明.zip 【项目资源说明】 使用多线程异步操作rknn模型, 提高rk3588/rk3588s的NPU使用率, 进而提高推理帧数(rk3568之类修改后应该也能使用,...
使用委托事件模拟多线程下载网络图片,即同时发送多个网络请求下载图片。 也可应用于其他异步多线程执行事件。
1. SpringBoot 自定义线程池以及多线程间的异步调用(@Async、@EnableAsync) 2.Java多线程之定时任务 以及 SpringBoot多线程实现定时任务 3.@EnableScheduling 与 @Scheduled
用于调用进度条类,多线程显示结果,否则进度条不会刷新
2. 采用handler+Thread模式实现多线程异步加载; 3. 引入线程池来管理多线程 4. 引入线程池,并引入内存缓存功能,并对外部调用封装了接口,简化调用过程 参考:...
应一个朋友要求,写一个动画WPF Ui加载动画、后台多线程加载数据,委托修改Ui状态 的一个实例,分享给大家。 有什么问题可以直接加WPF/Silverlight 群 204882221 找晴天就可以。 乐意帮你们处理WPF 任何问题。
支持多个客户端一起调用,支持多线程、异步数据处理
很专业的一个报表转换,多线程,异步AsynCallBack,委托的异步调用,winform皮肤控件,excel模版导出,导入,字符串的解析等高级技术完成的,本人原创,
对原有的dubbo远程调用的异步的缺陷性进行了优化方案