麻了,代码改成多线程,竟有9大问题

本文探讨了将代码改为多线程执行后可能遇到的9个问题,包括无法获取返回值、数据丢失、顺序问题、线程安全、ThreadLocal异常、OOM、CPU使用率过高、事务问题以及可能导致服务崩溃的情况。解决方案包括使用CompletableFuture、线程同步工具以及合理控制并发量等。
摘要由CSDN通过智能技术生成

前言

很多时候,我们为了提升接口的性能,会把之前 单线程同步 执行的代码,改成 多线程异步 执行。

比如:查询用户信息接口,需要返回用户基本信息、积分信息、成长值信息,而用户、积分和成长值,需要调用不同的接口获取数据。

如果查询用户信息接口, 同步调用 三个接口获取数据,会非常耗时。

这就非常有必要把三个接口调用,改成 异步调用 ,最后 汇总结果 。

再比如:注册用户接口,该接口主要包含:写用户表,分配权限,配置用户导航页,发通知消息等功能。

该用户注册接口包含的业务逻辑比较多,如果在接口中同步执行这些代码,该接口响应时间会非常慢。

这时就需要把业务逻辑梳理一下,划分: 核心逻辑 和 非核心逻辑 。这个例子中的核心逻辑是:写用户表和分配权限,非核心逻辑是:配置用户导航页和发通知消息。

显然 核心逻辑 必须在接口中 同步执行 ,而 非核心逻辑 可以 多线程异步 执行。

等等。

需要使用多线程的业务场景太多了,使用多线程异步执行的好处不言而喻。

但我要说的是,如果多线程没有使用好,它也会给我们带来很多意想不到的问题,不信往后继续看。

今天跟大家一起聊聊,代码改成多线程调用之后,带来的9大问题。

1.获取不到返回值

如果你通过直接继承 Thread 类,或者实现 Runnable 接口的方式去创建 线程 。

那么,恭喜你,你将没法获取该线程方法的返回值。

使用线程的场景有两种:

  1. 不需要关注线程方法的返回值。
  2. 需要关注线程方法的返回值。

大部分业务场景是不需要关注线程方法返回值的,但如果我们有些业务需要关注线程方法的返回值该怎么处理呢?

查询用户信息接口,需要返回用户基本信息、积分信息、成长值信息,而用户、积分和成长值,需要调用不同的接口获取数据。

如下图所示:

在Java8之前可以通过实现 Callable 接口,获取线程返回结果。

Java8以后通过 CompleteFuture 类实现该功能。我们这里以CompleteFuture为例:

public UserInfo getUserInfo(Long id) throws InterruptedException, ExecutionException {
    final UserInfo userInfo = new UserInfo();
    CompletableFuture userFuture = CompletableFuture.supplyAsync(() -> {
        getRemoteUserAndFill(id, userInfo);
        return Boolean.TRUE;
    }, executor);

    CompletableFuture bonusFuture = CompletableFuture.supplyAsync(() -> {
        getRemoteBonusAndFill(id, userInfo);
        return Boolean.TRUE;
    }, executor);

    CompletableFuture growthFuture = CompletableFuture.supplyAsync(() -> {
        getRemoteGrowthAndFill(id, userInfo);
        return Boolean.TRUE;
    }, executor);
    CompletableFuture.allOf(userFuture, bonusFuture, growthFuture).join();

    userFuture.get();
    bonusFuture.get();
    growthFuture.get();

    return userInfo;
}

温馨提醒一下,这两种方式别忘了使用线程池。示例中我用到了executor,表示自定义的线程池,为了防止高并发场景下,出现线程过多的问题。

此外, Fork/join 框架也提供了执行任务并返回结果的能力。

2.数据丢失

我们还是以注册用户接口为例,该接口主要包含:写用户表,分配权限,配置用户导航页,发通知消息等功能。

其中:写用户表和分配权限功能,需要在一个事务中同步执行。而剩余的配置用户导航页和发通知消息功能,使用多线程异步执行。

表面上看起来没问题。

但如果前面的写用户表和分配权限功能成功了,用户注册接口就直接返回成功了。

但如果后面异步执行的配置用户导航页,或发通知消息功能失败了,怎么办?

如下图所示:

该接口前面明明已经提示用户成功了,但结果后面又有一部分功能在多线程异步执行中失败了。

这时该如何处理呢?

没错,你可以做 失败重试 。

但如果重试了一定的次数,还是没有成功,这条请求数据该如何处理呢?如果不做任何处理,该数据是不是就丢掉了?

为了防止数据丢失,可以用如下方案:

  1. 使用mq异步处理。在分配权限之后,发送一条mq消息,到mq服务器,然后在mq的消费者
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值