Mor*_*itz 23 java multithreading final volatile
如果我有一个在线程之间共享的对象,在我看来每个字段应该是final或者volatile,具有以下推理:
如果应该更改字段(指向另一个对象,更新原始值),那么该字段应该是volatile所有其他线程对新值进行操作.仅仅访问所述字段的方法的同步是不够的,因为它们可能返回缓存的值.
如果该领域永远不会改变,那就去做吧final.
但是,我找不到任何关于此的内容,所以我想知道这个逻辑是否有缺陷还是太明显?
当然编辑而不是易失性可能使用final AtomicReference或类似.
编辑,例如,请参阅get getter方法是Java中volatile的替代方法吗?
编辑以避免混淆:这个问题是关于缓存失效!如果两个线程对同一个对象进行操作,则可以缓存对象的字段(每个线程),如果它们未声明为volatile.如何保证缓存无效?
最后的编辑感谢@Peter Lawrey,他指出了JLS§17(Java内存模型).据我所知,它表明同步在操作之间建立了先发生关系,因此如果那些更新"发生在之前",则线程会看到来自另一个线程的更新,例如,如果非易失性字段的getter和setter是synchronized.
Pet*_*rey 30
虽然我觉得private final应该是字段和变量的默认值,使用关键字var使其变得可变,但在不需要时使用volatile是
final,通过说不应该改变它来提高清晰度,使用volatile不需要的时候,可能会引起混淆,因为读者试图弄清楚为什么它变得易变.如果应该更改字段(指向另一个对象,更新原始值),则该字段应该是volatile,以便所有其他线程对新值进行操作.
虽然这对于读取是好的,但请考虑这个简单的情况.
volatile int x;
x++;
Run Code Online (Sandbox Code Playgroud)
这不是线程安全的.因为它是一样的
int x2 = x;
x2 = x2 + 1; // multiple threads could be executing on the same value at this point.
x = x2;
Run Code Online (Sandbox Code Playgroud)
更糟糕的是,使用volatile会使这种bug更难找到.
随着yshavit点的出现,更新多个字段更难以解决,volatile例如HashMap.put(a, b)更新多个引用.
仅仅访问所述字段的方法的同步是不够的,因为它们可能返回缓存的值.
synchronized给你所有的内存保证volatile和更多,这就是为什么它明显变慢.
注意:只是synchronized每个方法并不总是足够的.StringBuffer每个方法都是同步的,但在多线程上下文中是无用的,因为它的使用很可能容易出错.
很容易认为实现线程安全就像洒上仙尘一样,添加一些神奇的线程安全,你的bug就会消失.问题是螺纹安全更像是一个有很多孔的铲斗.插入最大的孔并且这些漏洞看起来会消失,但除非你全部插上它们,否则你没有线程安全,但它可能更难找到.
就同步与易失性而言,这表明了这一点
其他机制,例如volatile变量的读写和java.util.concurrent包中类的使用,提供了其他同步方法.
https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html
final无论线程问题如何,创建不需要更改的字段都是一个好主意.它使类的实例更容易推理,因为你可以更容易地知道它的状态.
在制作其他领域方面volatile:
仅仅访问所述字段的方法的同步是不够的,因为它们可能返回缓存的值.
如果访问synchronized块之外的值,则只能看到缓存的值.
所有访问都需要正确同步.保证在另一个同步块的启动之前(在同一监视器上同步时)发生一个同步块的结束.
至少有几种情况你仍然需要使用同步:
Atomic*类而不是"普通旧字段"; 但即使对于单个字段更新,您仍然可能需要独占访问权限(例如,将一个元素添加到列表中,同时删除另一个元素).ArrayList数组或数组.