【问题标题】:Flux visibility problems with volatile variablevolatile 变量的通量可见性问题
【发布时间】:2022-01-20 09:50:29
【问题描述】:

我有一个简单的 Spring 控制器,它有一个 Spring 服务作为依赖项。 在服务类中,我有一个名为 flag 的 int 类型的静态 volatile 字段。 当我通过控制器调用 createFlux() 方法时,标志设置为 5,然后创建一个新的 Flux,它每秒检查一次标志,并根据标志值打印一条消息。由于 delayElements 方法语义,代码将并行执行。之后,如果我调用 changeFlag() 方法,该方法会更改标志的值,并且由于标志变量是易变的,我希望打印的消息会发生变化,但这不会发生。

代码如下:

@RestController
public class MyController {

  @Autowired private MyService myService;

  @GetMapping("createFlux")
  public void createFlux() {
    myService.createFlux();
  }

  @GetMapping("changeFlag")
  public void changeFlag() {
    myService.changeFlag();
  }
}


@Service
public class MyService {

  private static volatile int flag = 3;

  public void changeFlag() {
    flag = 3;
    System.out.println("############# Flag = " + flag);
  }

  public void createFlux() {
    flag = 5;
    System.out.println("Flag = " + flag);

    Flux.generate(sink -> {
      if (flag == 3) {
        sink.next("Stop");
      } else {
        sink.next("Start");
      }
    }).delayElements(Duration.ofSeconds(1)).subscribe(s -> System.out.println(Thread.currentThread().getName() + " : " + s));
  }
}

这是控制台的输出:

Flag = 5
parallel-1 : Start
parallel-2 : Start
parallel-3 : Start
parallel-4 : Start
############# Flag = 3
parallel-5 : Start
parallel-6 : Start
parallel-7 : Start
parallel-8 : Start
parallel-1 : Start
parallel-2 : Start
parallel-3 : Start
parallel-4 : Start
parallel-5 : Start
parallel-6 : Start
parallel-7 : Start
parallel-8 : Start
parallel-1 : Start
parallel-2 : Start
parallel-3 : Start
parallel-4 : Start
parallel-5 : Start
parallel-6 : Start
parallel-7 : Start
parallel-8 : Start
parallel-1 : Start
parallel-2 : Start
parallel-3 : Start
parallel-4 : Start
parallel-5 : Start
parallel-6 : Start
parallel-7 : Start
parallel-8 : Start
parallel-1 : Stop
parallel-2 : Stop
parallel-3 : Stop
parallel-4 : Stop
parallel-5 : Stop
parallel-6 : Stop

从输出可以看出,即使 flag 的值更改为 3,它也会继续打印消息 Start。一段时间后,打印的消息也发生了变化。我想有一些缓存或类似的东西,但是 volatile 变量没有被缓存。

问题是 - 这是一个错误还是我遗漏了什么?

【问题讨论】:

  • 你能不能试着把这个变量变成一个实例变量,看看输出是否改变?
  • 没有变化。不管是不是静态的,结果都是一样的。
  • 你能把int改成Integer再试一次吗?
  • 仍然没有变化。使用 Integer 没有意义,但使用 int 没有意义。

标签: java multithreading project-reactor


【解决方案1】:

为一堆元素立即执行生成消费者。您可以通过在generate 之后添加日志来轻松确认:

    Flux.generate(...).doOnNext(e -> log.info("executed: {}", e))

打印:

2022-01-20 13:31:50,346  INFO parallel-1  - Flag = 5
2022-01-20 13:31:50,349  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,351  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,352  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,352  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,352  INFO parallel-1  - executed: Start
2022-01-20 13:31:50,352  INFO parallel-1  - executed: Start

generate 方法根据下游的需求发出元素。它首先生成 32 个元素并缓冲它们。当下游开始处理元素并且缓冲区大小低于阈值时,它会发出更多元素。

【讨论】:

  • 没错。我什至可以调试它。谢谢你:)
猜你喜欢
  • 2021-04-01
  • 1970-01-01
  • 2017-08-09
  • 2011-07-30
  • 2015-07-06
  • 1970-01-01
  • 1970-01-01
  • 2020-09-06
  • 2015-10-20
相关资源
最近更新 更多