如果我有一个变量从多个线程读取并且只有一个线程写入,我是否需要锁定该变量?如果一个线程试图读取而另一个线程试图同时写入,它会崩溃吗?
并发问题不是崩溃,而是你看到的数据版本.
如果共享变量是以原子方式编写的,那么当您认为(编写者)线程更新了变量时,一个(读者)线程可能会读取过时的值.您可以使用volatile关键字来防止读者线程在这种情况下读取过时的值.
如果写操作不是原子的(例如,如果它是某种类型的复合对象,并且您一次写入它的位,而其他线程理论上可以读取它)那么您的关注点也会是某些读者线程可以看到变量处于不一致状态.您可以通过在变量写入时锁定对变量的访问(缓慢)或确保您以原子方式编写来阻止此操作.
写入某些类型的字段是原子,但没有一个之前发生,确保正确的存储排序(除非你使用的关系volatile); 请参阅此页面了解详情.
简单的答案是肯定的,你需要同步。
如果您在没有某种形式的同步的情况下写入字段并从其他任何地方读取它,您的程序可能会看到不一致的状态,并且可能是错误的。您的程序不会崩溃,但可以看到旧数据或新数据,或者(在长整型和双精度型的情况下)一半旧数据和一半新数据。
不过,当我说“某种形式的同步”时,更准确地说,我指的是在写入和读取位置之间创建“先于发生”关系(也称为内存屏障)的东西。同步或 java.util.concurrent.lock 类是创建此类事物的最明显方法,但所有并发集合通常也提供类似的保证(请检查 javadoc 以确保)。例如,在并发队列上执行 put 和 take 操作将创建一个happens-before 关系。
将字段标记为易失性可以防止您看到不一致的引用(长时间撕裂),并保证所有线程都会“看到”写入。但易失性字段写入/读取不能与较大原子单元中的其他操作组合。原子类处理常见的组合操作,例如比较和设置或读取和递增。同步或其他 java.util.concurrent 同步器(CyclicBarrier 等)或锁应该用于更大的排他性区域。
与简单的“是”不同,有些情况更多的是“不,如果你真的知道自己在做什么”。两个例子:
1) 字段的特殊情况是最终的并且仅在构造期间写入。其中一个示例是当您填充预先计算的缓存时(想象一个映射,其中键是众所周知的值,值是预先计算的派生值)。如果您在构造之前在字段中构建该字段,并且该字段是最终字段并且您以后不再写入它,则构造函数的末尾将执行“最终字段冻结”,并且后续读取不需要同步。
2) 《Effective Java》中介绍的“racy single check”模式的情况。规范的示例位于 java.lang.String.hashCode() 中。String 有一个哈希字段,它在您第一次调用 hashCode() 时延迟计算并缓存到本地字段中,该字段不同步。基本上,多个线程可能会竞相计算该值并设置在其他线程之上,但因为它由众所周知的哨兵 (0) 保护并且始终计算相同的值(因此我们不关心哪个线程“获胜”或是否多次执行),这实际上保证没问题。
更长的参考(由我编写):http ://refcardz.dzone.com/refcardz/core-java-concurrency
| 归档时间: |
|
| 查看次数: |
5748 次 |
| 最近记录: |