String
对象是不可变的,所以像 =
和 +=
这样的运算符只会修改引用。因此,对于 String
,volatile 是安全的,因为它适用于引用本身。这同样适用于其他不可变对象,就像基元类型一样。String
上,+=
本身也不是线程安全的,因为它不是原子性的,包含一个读操作和一个写操作。如果在读和写之间对 String
对象进行了更改,则可能导致意外结果。虽然生成的 String
仍然有效,但它可能具有意外的值。特别地,某些更改可能会“覆盖”其他更改。例如,如果您有一个值为 "Stack "
的 String
,一个线程尝试添加 "Overflow"
,而另一个线程尝试添加 "Exchange"
,则有可能只应用一个更改。这同样适用于基元类型。如果您感兴趣,可以在 这里 找到有关这个特定问题的更多详细信息(主要是在基元类型的上下文中)。+=
添加同步功能,则最简单的方法是使用 AtomicReference
进行同步,这种情况下就不需要使用 volatile
。 - Bohemianvolatile
对于 String
是安全的,但是在使用 +=
运算符时就不安全了?我实际上喜欢 @Bohemian 建议的 AtomicReference
的想法。听起来比 volatile
本身不那么令人困惑。 - jarosAtomicReference
提供了 getAndUpdate() 方法,以线程安全的方式应用更新。 - Bohemian+=
是线程安全的。因为它们是不可变的,所以没有任何东西可以“影响”它们。+=
操作不是原子性的,因此如果多个线程同时使用+=
,则可能会覆盖一些附加的文本,但您始终会看到某些 功能正常的String对象。 - Boann+=
在线程安全方面存在问题,竞态条件可能会导致意外结果。虽然 String 对象仍然可用,但如果涉及多个线程,则其值可能与预期完全不同。我将编辑我的答案以更清楚地表达这一点。 - Evan Baileylock
或synchronize
混淆。
没有必要这样做。private String ref =“some-string”;
现在很可能会有一个线程修改引用ref=“some-other-string”
。此外,答案必须详细说明并添加超过注释的价值。单行注释也可以成为注释的一部分。 - nits.kkprivate String ref = "some-string";
这样的语句和一个方法 public void set(String s){ ref = s;}
,现在线程 T1 调用了 set("Abc")
,线程 T2 调用了 set("123")
,同时线程 T3 在一个 while 循环中读取该字符串,你认为会出现什么行为? - nits.kka
的值是什么意思?你确定在没有任何同步构造和未声明为 volatile 的情况下,当你检查 a 的值时,a 的值是从内存中读取而不是线程本地缓存吗? - nits.kk