我自己已经用 java 写了很多年了,但只是作为一种爱好,我不必经常使用多线程。
有几个问题很有趣,即:
假设我需要在某个对象(来自不同线程)上编写和读取原始类型。假设这些字段是打开的 (
public)。根据java帮助,读写引用的操作是原子的(逻辑上,因为这种引用的大小通常放在一个处理器寄存器中,系统支持)。然后读取和写入存储对对象的引用的字段是在不同线程中执行的完全正常的操作。
此外,java 帮助确保读取和写入基本类型(不包括
longanddouble)也是原子的。但是这两个呢?据我了解,它们只需要标记为volatile. 但是是否有任何副作用,或者这个修饰符只是保证了对变量的操作的原子性?好的。现在,例如,我想通过 getters-setter 读取和写入字段。如果我的 getter-setter 不改变任何外部数据的状态,而只是读取和写入一个变量,我是否足以将这样的变量标记为
volatile,或者我应该将 getter/setter 标记为syncronized?(前提是只能通过 getter 和 setter 访问变量)。处理对象。具体来说,对于不改变状态的对象。
例如,
String。据我所知,这种类型永远不会改变 state,它所有返回字符串的方法都会返回一个新实例。那么,我可以在没有额外同步的情况下从不同的线程访问这样的对象吗?或者我可能会错过一些东西(当我只能获取对象但不能更改引用它的字段的值时的问题)。
感谢可以回答这些问题的人的耐心)
重要的是它
volatile保证了可见性。一个线程中对 volatile 字段的更改将立即在另一个线程中可见。如果您需要线程安全地执行比赋值更复杂的过程,请使用同步或j.u.c.Atomic*-types 来避免竞争条件。示例:任何多线程计数器
请记住
synchronized- 这始终是一个显式锁定(但它非常便宜且竞争低),并且 Atomic 原语使用带有 CAS 指令的无限循环并且可以很好地优化。具有不可变状态的对象(例如,此类对象的所有字段
final)称为不可变对象,可以安全地在不同线程中使用它们。但是,如果对象的内部状态包含某种可变组件并且可以通过某种方式从外部访问,则您将不得不另外考虑线程安全。例子:第一个类是完全线程安全的。第二个 - 没有。该方法
getDate()将内部状态发布到外部代码(不安全发布)。在外面的某个地方,你可以通过改变内部状态来做到这一点:您可以保护自己免受这种情况的影响,例如,通过创建副本: