在 jruby 中,复合表达式 like||=
不是原子执行的。当你写:
foo ||= 'bar'
内部实际执行的内容类似于:
1. unless foo
2. foo = 'bar'
3. end
因为第 1 行和第 2 行是分开评估的,所以在多线程应用程序中,状态可能会被这两者之间的不同线程更改,例如:
thread 1: foo ||= 'bar'
thread 2: foo ||= 'baz'
执行如下:
# foo has not been set yet
1. thread 1: unless foo
2. thread 2: unless foo
3. thread 1: foo = 'bar'
4. thread 2: foo = 'baz'
# ...
请注意, foo 最终会被第二个线程重新分配给“baz”,即使它已经有一个值。使用+=
同样有问题,因为:
thread 1: x += 1
thread 2: x += 1
会这样执行:
# x starting value of 0
1. thread1: tempvar = x + 1 # 1
2. thread2: tempvar = x + 1 # 1
3. thread1: x = tempvar # 1
4. thread2: x = tempvar # 1
所以x
在两次操作之后应该是 2 但实际上只增加了一次。
如果您在 jruby 中运行单线程应用程序/脚本,这都不是问题。如果您要使用多个线程运行,那么如果这些操作用于由多个线程访问的变量,则这些操作在这些线程的执行环境中使用是不安全的。
在线程安全很重要的环境中,您可以通过将操作包装在互斥锁中或使用确保特定操作的线程安全的原语来解决此问题。
atomic gem也可以与 jRuby 一起使用,以确保检查和更新操作的原子性。我不确定它是否支持数组。
有关在 jRuby 中管理并发的更多信息。
希望这可以帮助!