Java场同步

Java field synchronization

我熟悉synchronizedblock/method。从单独的线程修改字段时是否需要这样做?同时设置和获取不同步的字段会导致字段搞砸吗?这是一个小图表:

Time | Thread A    | Thread B
1      ...           ...
2      fieldA = 10;  System.out.println(fieldA);
3      fieldA = 20;  fieldA = 30;
4      ...           ...

这样的事情会导致某种异常吗?多线程字段应该总是通过同步访问吗?当使用对象与基元作为字段类型时,这种行为会改变吗?

阅读 this,它使我确信两个线程具有不同的内存,并且每个字段更改都被克隆到“共享”该字段的所有其他线程。

字节码指令会以奇怪的方式执行吗?就像说执行 ++ 的字节码是获取值、增加值并存储它。

Time | Thread A    | Thread B //Imagine in bytecode
1      x = 10        waiting
2      y = x + 1     waiting
3      waiting       x = 0;
4      waiting       ...
5      x = y         waiting

在上面的示例中,线程 A 将 x 从 10 递增到 11,但是线程 B 的 x = 0 被完全忽略了。如果这种类型的线程切换没有发生,而是逐个语句地进行,那么两个线程工作的结果要么是 0,要么是 1。

那么我的问题又来了,修改共享字段什么时候需要同步?如果需要或不需要同步,能否请您解释原因并附上上面的几个例子?

是的——这几乎是您必须同步的内容的教科书示例。

如果您想进入 JLS,chapter 17 拥有所有相关内容。部分摘录:

Two accesses to (reads of or writes to) the same variable are said to be conflicting if at least one of the accesses is a write.

...

When a program contains two conflicting accesses (§17.4.1) that are not ordered by a happens-before relationship, it is said to contain a data race.

...

A set of actions is sequentially consistent if all actions occur in a total order (the execution order) that is consistent with program order

...

If a program has no data races, then all executions of the program will appear to be sequentially consistent.

换句话说:

  • 如果您跨线程读取和写入同一字段,其中一个是写入,那么除非您同步访问,否则您将发生数据竞争
  • 如果你有数据竞争,几乎所有的赌注都没有了

比你提供的案例还要诡异。您可以使用代码 a = 10; b = 20;,一个线程将按照该顺序执行,而另一个线程将看到 b = 20; a = 10;。发生这种情况的原因是优化器重新排序、内存缓存以及使您的多核机器运行 快速 的任意数量的优化,但代价是混淆多线程行为。