The values for an object's final fields are set in its constructor. Assuming the object is constructed "correctly", once an object is constructed, the values assigned to the final fields in the constructor will be visible to all other threads without synchronization. In addition, the visible values for any other object or array referenced by those final fields will be at least as up-to-date as the final fields.數組
一個對象的final字段是在它的構造器中賦值的。假如這個對象被正確的構造了,那麼即便不用同步,其餘線程也能看到這個這個final字段的值。另外,引用這個final字段的對象或者數組都會即便更新。安全
What does it mean for an object to be properly constructed? It simply means that no reference to the object being constructed is allowed to "escape" during construction. (See Safe Construction Techniques for examples.) In other words, do not place a reference to the object being constructed anywhere where another thread might be able to see it; do not assign it to a static field, do not register it as a listener with any other object, and so on. These tasks should be done after the constructor completes, not in the constructor.併發
但什麼是正確的構造呢?簡而言之,就是在構造過程當中,不能有任何引用指向這個對象(參考安全構造技術示例)。換句話說,不要在其它線程能看到的地方引用一個正在構建的對象,不要將引用設成靜態字段,不要將引用與其它對象註冊成監聽器,等等。這些任務應該在構建完成以後,而不是在構造過程當中。函數
class FinalFieldExample { final int x; int y; static FinalFieldExample f; public FinalFieldExample() { x = 3; y = 4; } static void writer() { f = new FinalFieldExample(); } static void reader() { if (f != null) { int i = f.x; int j = f.y; } } }
The class above is an example of how final fields should be used. A thread executing reader is guaranteed to see the value 3 for f.x, because it is final. It is not guaranteed to see the value 4 for y, because it is not final. If FinalFieldExample's constructor looked like this:this
以上是final字段的正確用法。任何線程調用reader()獲得的x必定是3,由於x是final的,但y就不必定是4。若是上慄的構造器是下面這樣的:線程
public FinalFieldExample() { // bad! x = 3; y = 4; // bad construction - allowing this to escape global.obj = this; }
then threads that read the reference to this from global.obj are not guaranteed to see 3 for x.翻譯
那麼,因爲構造過程當中,表明當前對象的this被global.obj引用,x的值就不能保證必定是3了。(x=3, y=4 和 global.obj=this 可能會發生重排序)指針
The ability to see the correctly constructed value for the field is nice, but if the field itself is a reference, then you also want your code to see the up to date values for the object (or array) to which it points. If your field is a final field, this is also guaranteed. So, you can have a final pointer to an array and not have to worry about other threads seeing the correct values for the array reference, but incorrect values for the contents of the array. Again, by "correct" here, we mean "up to date as of the end of the object's constructor", not "the latest value available".code
像第二個構造器同樣,經過引用this,能在構造過程當中看到x和y是否被賦予正確的值是很棒的,可是若是x(或y)自己就是一個引用,並且你想要看到x所指向的最新值,那麼x必須是final的。也就是說,你能夠用一個final指針指向一個數組,其餘線程看到的引用必定正確,但數組的值可能不正確。正確的意思,是指對象構造結束後就是最新值,而不是最近的可用值。對象
Now, having said all of this, if, after a thread constructs an immutable object (that is, an object that only contains final fields), you want to ensure that it is seen correctly by all of the other thread, you still typically need to use synchronization. There is no other way to ensure, for example, that the reference to the immutable object will be seen by the second thread. The guarantees the program gets from final fields should be carefully tempered with a deep and careful understanding of how concurrency is managed in your code.
說了這麼多,若是你用一條線程構造了一個不可變對象(全部字段都爲final),想要讓它對其餘線程正確地可見,仍是應該用同步的,由於沒有其餘方法能夠保證,該對象的引用會被另外一條線程看到。 併發中要想看到final的正確值,應該深思熟慮。
There is no defined behavior if you want to use JNI to change final fields.
JMM對使用JNI修改final字段沒有規定。
翻譯完發現仍是不懂,又找到一篇文章《深刻理解 Java final 變量的內存模型》,final的介紹很詳細。
這裏總結一下從中抽出的關鍵點:
JMM禁止編譯器把final字段的寫,重排序到構造器外面
編譯器會在final字段的寫以後,構造函數的return以前,插入一個StoreStore屏障,這個屏障禁止處理器把final的寫重排序到構造函數以外
編譯器會在讀final字段操做的前面插入一個LoadLoad屏障
在一個線程中,初次讀對象引用與初次讀該對象包含的final字段,JMM禁止處理器重排序這兩個操做