【问题标题】:Is marking String type reference as Volatile safe?将字符串类型引用标记为易失性安全吗?
【发布时间】:2020-05-06 06:21:01
【问题描述】:

我读过一些帖子和文章说我们不应该将 java 对象声明为 volatile,因为结果,只有引用变为 volatile。以下是一些示例:

link-1 link-2 link-3

Sonar 的建议是“非原始字段不应该是“易失性的””,但是,它也表明所描述的问题是指可变对象“同样,将可变对象字段标记为 volatile 意味着对象引用是易失性的,但对象本身不是'。

我的问题是:将 java String 声明为 volatile 是否安全?

【问题讨论】:

  • 您的标题含糊不清。重写以总结您的具体技术问题。
  • 不太清楚你所说的“安全”是什么意思。 volatile 关键字完全按照它为所有类型的字段指定的操作,无论它们是否指向对象。例如,它不会爆炸。而且因为 String 实例是不可变的,所以它们本身不会因被多个线程使用而发生故障,无论您对它们做什么或是否使用 volatile 字段来执行它。但这并不意味着 volatile 所做的事情足以 让应用程序按照您的意愿运行。随处添加 volatile 不会神奇地使应用程序线程安全。
  • 很抱歉。这个标题是由版主设置的,不是我。

标签: java string immutability volatile


【解决方案1】:

因为String 对象是不可变的,所以只有引用被=+= 等运算符修改。因此,volatile 对于String 是安全的,因为它适用于引用本身。这也适用于其他不可变对象,就像它适用于基元一样。

澄清:

+= 本身即使在 volatile String 上也不是线程安全的,因为它不是原子的,并且由读取和写入组成。如果在读写之间影响String 对象,可能会导致意外结果。虽然生成的 String 仍然有效,但它可能具有意外值。特别是,某些更改可能会“覆盖”其他更改。例如,如果您有一个值为"Stack "String,并且一个线程尝试追加"Overflow",而另一个线程尝试追加"Exchange",则可能只会应用一个更改。这也适用于原语。如果您有兴趣,可以在here 找到有关此特定问题的更多详细信息(主要是在原语的上下文中)。

【讨论】:

  • 是的,您必须将同步添加到+=,使用AtomicReference 最简单,在这种情况下您不需要volatile
  • 所以volatileString 是安全的,但在使用+= 运算符时不安全?我实际上喜欢@Bohemian 建议的AtomicReference 的想法。听起来不像volatile 本身那么令人困惑。
  • @jaros 正常的字符串连接是一个非线程安全的操作,因为另一个线程在读取它之后但在写入新值之前修改了值/引用。 AtomicReference 提供了getAndUpdate() 方法,该方法以线程安全的方式应用更新。
  • 您的“澄清”使事情变得混乱。 += 线程安全的,如果用“线程安全”,我们的意思是 String 对象本身不会在内部发生故障。没有什么可以“影响”它们,因为它们是不可变的。 += 操作不是atomic,所以如果多个线程同时使用+=,你可能会覆盖一些附加的文本,但你仍然会看到some functioning 之后的字符串对象。
  • @Boann 我的意思是+= 不是线程安全的,因为竞争条件会导致意外结果。虽然 String 对象仍然有效,但它可能具有与涉及多个线程时所预期的完全不同的值。我将编辑我的答案以更清楚地说明这一点。
【解决方案2】:

Java String 是 final 类,不可变且线程安全。
String 没有中间状态,在多线程情况下不会与locksynchronize 混淆。 没有必要这样做。

【讨论】:

  • 这是什么意思?是的,字符串在上下文中是线程安全的,其内部状态是不可变的。参考呢。如果你有一个实例变量private String ref = "some-string"; 现在线程很可能会修改引用ref = "some-other-string"。此外,答案必须有细节,并且应该比评论更有价值。单个衬垫很可能成为 cmets 的一部分。
  • 现在private String ref = "some-string"; 一个方法public void set(String s){ ref = s;} 怎么样 线程T1 调用set("Abc") 和线程T2 调用set("123") 和线程T3 在while 循环中读取字符串,你怎么看,可能是行为。 ?
  • 此外,请阅读我的第一条评论。它不是关于对内部状态的保护,而是关于引用的变量。除非引用被标记为 static final ,否则您没有 String 类型的引用作为线程安全的。字符串对象是线程安全的,而不是它的非静态最终引用
  • volatile 保证任何读取都来自内存而不是线程缓存。当你阅读任何参考文献时,它的值可以改变或不决定 volatile 的使用。
  • 检查a值是什么意思?您确定没有任何同步结构,并且没有将其声明为 volatile ,当您 检查 a 的值 时 a 的值是从内存中读取的,而不是线程本地缓存?
猜你喜欢
  • 2021-03-30
  • 2010-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-22
  • 1970-01-01
  • 2023-03-10
  • 2019-12-12
相关资源
最近更新 更多