• java异步编程


    Q:为何要采用异步编程

    A:异步编程首先不会节约线程,因为异步操作都会重新开一个线程。异步编程是提高了CPU的使用率,采用同步编程的方式,整个服务器的所有线程大部分都没有在工作,而是在等待。因为线程同步操作

    要等整个事件处理完成才能提交,所以CPU的利用率很低;当采用异步编程,线程不需要等待,减少时间分片的占用,提高系统的吞吐量。在超高请求数量场景下,异步的实现不再需要线程等待执行结果,只需要个位数量的线程,即可实现同步场景大量线程一样的吞吐量。

    举例:

       实现一个转账的微服务 Transfer( accountFrom, accountTo, amount),这个服务有三个参数:

    分别是转出账户、转入账户和转账金额。

      实现过程也比较简单,我们要从账户 A 中转账 100 元到账户 B 中:先从 A 的账户中减去 100 元;再给 B 的账户加上 100 元,转账完成。

      采用同步操作:

      


    Transfer(accountFrom, accountTo, amount) {
    // 先从accountFrom的账户中减去相应的钱数
    Add(accountFrom, -1 * amount)
    // 再把减去的钱数加到accountTo的账户中
    Add(accountTo, amount)
    return OK
    }

      同步操作我们很容易想到的,那同步操作的效率怎么样呢? 

      假设内部add方法执行效率在50ms,那一个线程执行烧水泡茶的时延在100ms,按照这种实现,每个线程每分钟最多处理10个请求,假设我们使用的服务器同时打开的线程数量上限是 10,000,
    可以计算出这台服务器每秒钟可以处理的请求上限是: 10,000 (个线程)* 10(次请求每秒) = 100,000 次每秒。如果请求速度超过这个值,那么请求就不能被马上处理,只能阻塞或者排队,
    这时候 Transfer 服务的响应时延由 100ms 延长到了:排队的等待时延 + 处理时延 (100ms)。也就是说,在大量请求的情况下,我们的微服务的平均响应时延变长了。 这已经到CPU的极限了吗?
    其实不然,如果我们监测一下服务器的各项指标,会发现无论是 CPU、内存,还是网卡流量或者是磁盘的 IO 都空闲的很,多大部分线程都在等待每一个服务返回结果。

      
      

      采用异步操作:

       是在线程模型上由同步顺序调用改为了异步调用和回调的机制。

      接下来,我们来看下,如何用 CompletableFuture 实现的转账服务。

      首先,我们用 CompletableFuture 定义 2 个微服务的接口:

      

    /**
     * 账户服务
     */
    public interface AccountService {
        /**
         * 变更账户金额
         * @param account 账户ID
         * @param amount 增加的金额,负值为减少
         */
        CompletableFuture<Void> add(int account, int amount);
    }
    
    /**
     * 转账服务
     */
    public interface TransferService {
        /**
         * 异步转账服务
         * @param fromAccount 转出账户
         * @param toAccount 转入账户
         * @param amount 转账金额,单位分
         */
        CompletableFuture<Void> transfer(int fromAccount, int toAccount, int amount);
    }
    
    /**
     * 转账服务的实现
     */
    public class TransferServiceImpl implements TransferService {
        @Inject
        private  AccountService accountService; // 使用依赖注入获取账户服务的实例
        @Override
        public CompletableFuture<Void> transfer(int fromAccount, int toAccount, int amount) {
          // 异步调用add方法从fromAccount扣减相应金额
          return accountService.add(fromAccount, -1 * amount)
          // 然后调用add方法给toAccount增加相应金额
          .thenCompose(v -> accountService.add(toAccount, amount));    
        }
    }

      我们先调用一次账户服务 accountService.add() 方法从 fromAccount 扣减响应的金额,因为 add() 方法返回的就是一个 CompletableFeture 对象,可以用 CompletableFeture 的 thenCompose() 方法将下一次调用 accountService.add() 串联起来,实现异步依次调用两次账户服务完整转账。

  • 相关阅读:
    jS Ajax 上传文件报错"Uncaught TypeError: Illegal invocation"
    layer实现关闭弹出层刷新父界面功能详解
    PHP开发APP接口简单签名全过程(二)实际测试
    PHP开发APP接口简单签名全过程(一)
    Laravel 避免 Trying to get property of non-object 错误的六种方法 [新增第六种 data_get]
    在PHP代码中将HTML代码原样输出的方式
    在使用 Laravel Eloquent 模型时,我们要判断取出的结果集是否为空,但我们发现直接使用 is_null 或 empty是无法判段它结果集是否为空的!!!
    使用layer的iframe层提交表单后,需要关闭当前的iframe层,然后刷新父页面的方法
    laravel中如何实现验证码验证及使用
    递归思想的由来
  • 原文地址:https://www.cnblogs.com/volare/p/12287845.html
Copyright © 2020-2023  润新知