异步的方式以及它使用的几个场景

异步的方式

异步用在同一个线程中的场景也有,但是我遇到的不多,我遇到的更多的是把异步用在不同的线程中。
现实开发中我使用到异步的场景一般都会牵涉到两个线程,在主线程中有它主要的执行逻辑,然后在副线程中执行异步操作。有下面几个场景:

1.提高主线程的运行速率,把耗时操作放到另外一个线程异步执行

使用异步的第一个开发场景就是,有时候我们如果只用一个主线程执行速度太慢了,需要大量的等待耗时操作,很耗费时间,所以这个时候就可以多引入一个线程,把主线程中耗时的操作单独执行,这样在执行耗时操作的同时,我们可以异步的执行主线程中的其他逻辑,可以大大提高程序运行时间。

比如现在我们有个需求场景,就是需要读取数据库里面的数据,这个过程非常耗时,比如我们需要读取Student学生表的大量的数据;然后还有个逻辑是,我们要进行100个数值计算,但是关于这100个数值计算与Student数据库表没有任何关系;第三步是让每行学生信息中的总分加上我们的100个数值计算的值;

那么现在我们分析一下上面的场景,耗时比较长的操作是什么?是从磁盘中读取数据库表数据到内存中;那么我们能不能把这一部分耗时的操作单独的抽取出来执行呢?

现在想一下假如我们只用一个主线程执行这段逻辑,那么这三部分我就必须要按照顺序执行,并且前面的部分还没有执行完毕的时候,后面的部分是不会被执行的。那假如说我们第一部分读取数据库表的操作非常的耗时,这个时候我们的整个程序运行的时间就会非常的长。那么能不能让程序执行的快一些呢?可以的,用什么办法呢?我们可以把执行时间比较长的操作,也就是读取数据库的操作放到另外一个线程中异步执行。相当于是cpu并发执行了两个线程,那有人可能会有这样的疑问,假如我现在cpu调度了副线程去读取mysql数据到磁盘中,假如这个时候切换线程,去执行主线程的其他的逻辑,比如计算100个数值,那这个时候我们仍然没有执行mysql读取的操作啊?后面仍然是要切换线程到副线程去读取mysql数据库的数据啊?程序总的消耗时间是一样的啊?其实我刚开始也有这样的顾虑,那是因为他们可能和我一样弄错了一个概念,就是当cpu调度副线程读取数据库数据,也就是把磁盘数据加载到内存中的时候,既变这个时候cpu再去调度其他的线程,我们的磁盘数据加载到内存也会一直在执行。这就像是有一个装满水的带有水塞的水槽,当你的大脑给手一个命令打开水塞的时候,当你打开水塞之后,水会一直从水槽往外流,既便这个时候你的大脑给脚一个跑的命令,水槽里的水仍然是在向外流的一样。
因此两个线程在某些情况下一定是比一个线程执行效率要高的。

那再次回到我们上面的例子中,怎么能让程序执行的快一些呢?我们就可以创建两个线程,把读取数据库的耗时操作单独的放到副线程中异步执行,在主线程中执行其他逻辑,最后当副线程异步执行的结果出来之后给主线程就可以,代码如下:

import java.util.concurrent.*;

public class Main {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(10); // 创建线程池
        Future<Integer> future = executor.submit(() -> {
            // 模拟读取数据库耗时操作
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return 100;
        });

        System.out.println("Main thread continues to work...");
        //第一个数值计算
        int result1 = method1();
        //第二个数值计算
        int result2 = method2();
        //第三......第100
        int result100 = method100();

        try {
            int baseDataValue = future.get(); // 等待异步操作完成并获取结果
            
            //获取异步结果之后,把异步结果分别和我们的数值计算结果相加
            baseDataValue + result1 .....
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        executor.shutdown(); // 关闭线程池
    }
}

上面的代码就可以大大提高我们程序的整体的运行效率,因为我们在执行数据库读取这个耗时操作的时候,是放到了另外一个副线程中执行的,在它读取数据的同时,我们仍然可以处理主线程中与数据库无关的操作,就是上面的一百个数值计算的操作,最后必须要用到数据库结果的时候再通过future.get方法获取到副线程中异步执行的结果就好了。

2.跨服获取数据,也可以用异步,不过使用的是CompletableFuture异步执行关键字

比如现在有两个服,一个server服,一个scene服,在scene服中想要异步获取到server服的数据,就可以使用CompletableFuture关键字。
代码如下:

	//下面是在scene服的代码
	class Test {
		public static void main(String[] args) {
			//查询参数
			int param = 11111;

			//执行主线程中的其他逻辑
			其他逻辑代码执行.....
			
			CompletableFuture<String> future = new CompletableFuture<>();
			//使用mq消息中间件给server服发送一个notice通知,告诉server服查询参数
			sendNotice(param);

			//得到从server服通过notice通知发送过来的数据
			int result = getDataFromNotice();

			//完成CompletableFuture异步
			future.complete(result);

			//主线程得到异步线程中的异步执行结果,从创建CompletableFuture到完成异步数据获取,这个时间不能超过1000ms,否则会主动抛出异常
			future.get(1000, TimeUnit.MILLISECONDS);
		} 
	}



	//下面是在server服的代码
	class ServerTest {
		public static void main(String[] args) {
			//reviceNotice
			int param = getParamFromScene();

			//根据参数得到server中的需要返回给scene服的数据
			int result = getDataByParam(param);

			//把数据返回给scene服
			sendNoticeToScene(result);
		}
	}

通过上图的代码,可以借助mq消息通知和CompletableFuture异步关键字跨服的访问另外一个服的数据,并且可以规定异步获取数据的最大时间,超过这个时间就会抛出异常。

  • 13
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr-X~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值