Springboot 中成员变量会出现线程抢占的情况

今天看到同事写的一段代码,感觉怪怪的,代码如下。Springboot 默认是单例模式,所有HTTP请求共用同一个对象,成员属性 testSingleton就会被共享,这样好像会导致响应出问题吧。但是,这个项目是已经上线的了,为什么没人发现这个问题呢?我开始怀疑我自己。。。

@RestController
public class TestController {

     String testSingleton = "操作成功";

    @GetMapping("/testSingleton1")
    public String testSingleton1() {
        testSingleton = "操作成功";
        return testSingleton;
    }

    @GetMapping("/testSingleton2")
    public String testSingleton2(){
        testSingleton = "操作失败";
        return testSingleton;
    }
}

所以,我决定要验证一下。为了更加直观,我改造了一下,加个极端环境Thread.sleep(10*1000)

@RestController
public class TestController {

     String testSingleton = "操作成功";

    @GetMapping("/testSingleton1")
    public String testSingleton1() throws InterruptedException {
        testSingleton = "操作成功";
        Thread.sleep(10*1000);
        return testSingleton;
    }

    @GetMapping("/testSingleton2")
    public String testSingleton2(){
        testSingleton = "操作失败";
        return testSingleton;
    }
}

先访问 /testSingleton1再迅速访问 /testSingleton2,测试结果如下:

操作失败

操作失败

显然,这个响应是有问题的,至此,打消了自己的怀疑。

------------------------------------------- 分割线 -------------------------------------------

那为什么没有用户发现这个问题呢?我想,原因有二:

  1. 大部分都是赋值后直接响应,没有Thread.sleep(10*1000)这么极端的场景,线程占用的情况不容易被复现
  2. 才几百个用户,而且该项目使用的频率比较低(心痛自己3秒钟,小丑竟是我自己。。。)

虽然问题目前还没暴露出来,但是问题确实存在,该如何解决呢?

  • 成员变量改为局部变量,局部变量不会引起线程的抢占,这是人们常说的无状态的。
@RestController
public class TestController {
    @GetMapping("/testSingleton1")
    public String testSingleton1() throws InterruptedException {
        String testSingleton = "操作成功";
        Thread.sleep(10*1000);
        return testSingleton;
    }

    @GetMapping("/testSingleton2")
    public String testSingleton2(){
        String testSingleton = "操作失败";
        return testSingleton;
    }
}

测试结果如下:

操作失败

操作成功  // 睡了10秒,后响应
  • 不使用单例模式,用原型模式来代替@Scope("prototype"),这样每次处理HTTP请求都会new一个新的对象,testSingleton就不会被共享,此时的成员变量也是无状态的。
@RestController
@Scope("prototype")
public class TestController {

     String testSingleton = "操作成功";

    @GetMapping("/testSingleton1")
    public String testSingleton1() throws InterruptedException {
        testSingleton = "操作成功";
        Thread.sleep(10*1000);
        return testSingleton;
    }

    @GetMapping("/testSingleton2")
    public String testSingleton2(){
        testSingleton = "操作失败";
        return testSingleton;
    }
}

测试结果如下:

操作失败

操作成功  // 睡了10秒,后响应

扩展:原型模式下,成员变量改为静态变量呢?

@RestController
@Scope("prototype")
public class TestController {

     public static String testSingleton = "操作成功";

    @GetMapping("/testSingleton1")
    public String testSingleton1() throws InterruptedException {
        testSingleton = "操作成功";
        Thread.sleep(10*1000);
        return testSingleton;
    }

    @GetMapping("/testSingleton2")
    public String testSingleton2(){
        testSingleton = "操作失败";
        return testSingleton;
    }
}

测试结果如下:

操作失败

操作失败

这个很好理解,静态变量被所有线程共享。

总结:在 Springboot 中,定义成员变量或者静态变量时要特别注意,是否会出现线程抢占的问题。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值