天天看點

Java記憶體模型FAQ(八到九)原文How can final fields appear to change their values?原文How do final fields work under the new JMM?

Java記憶體模型FAQ(八)Final字段如何改變它們的值

原文:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html 第八章

譯者:Alex

我們可以通過分析String類的實作具體細節來展示一個final變量是如何可以改變的。

String對象包含了三個字段:一個character數組,一個數組的offset和一個length。實作String類的基本原理為:它不僅僅擁有character數組,而且為了避免多餘的對象配置設定和拷貝,多個String和StringBuffer對象都會共享相同的character數組。是以,String.substring()方法能夠通過改變length和offset,而共享原始的character數組來建立一個新的String。對一個String來說,這些字段都是final型的字段。

String s1 = "/usr/tmp";
String s2 = s1.substring(4); 
           

字元串s2的offset的值為4,length的值為4。但是,在舊的記憶體模型下,對其他線程來說,看到offset擁有預設的值0是可能的,而且,稍後一點時間會看到正确的值4,好像字元串的值從“/usr”變成了“/tmp”一樣。

舊的Java記憶體模型允許這些行為,部分JVM已經展現出這樣的行為了。在新的Java記憶體模型裡面,這些是非法的。

原文

How can final fields appear to change their values?

One of the best examples of how final fields’ values can be seen to change involves one particular implementation of the String class.

A String can be implemented as an object with three fields — a character array, an offset into that array, and a length. The rationale for implementing String this way, instead of having only the character array, is that it lets multiple String and StringBufferobjects share the same character array and avoid additional object allocation and copying. So, for example, the method String.substring() can be implemented by creating a new string which shares the same character array with the original String and merely differs in the length and offset fields. For a String, these fields are all final fields.

String s1 = "/usr/tmp";
String s2 = s1.substring(4); 
           

The string s2 will have an offset of 4 and a length of 4. But, under the old model, it was possible for another thread to see the offset as having the default value of 0, and then later see the correct value of 4, it will appear as if the string “/usr” changes to “/tmp”.

The original Java Memory Model allowed this behavior; several JVMs have exhibited this behavior. The new Java Memory Model makes this illegal.

原創文章,轉載請注明: 轉載自并發程式設計網 – ifeve.com本文連結位址: Java記憶體模型FAQ(八)Final字段如何改變它們的值

Java記憶體模型FAQ(九)在新的Java記憶體模型中,final字段是如何工作的

原文:http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html 第九章

譯者:Alex

一個對象的final字段值是在它的構造方法裡面設定的。假設對象被正确的構造了,一旦對象被構造,在構造方法裡面設定給final字段的的值在沒有同步的情況下對所有其他的線程都會可見。另外,引用這些final字段的對象或數組都将會看到final字段的最新值。

對一個對象來說,被正确的構造是什麼意思呢?簡單來說,它意味着這個正在構造的對象的引用在構造期間沒有被允許逸出。(參見安全構造技術)。換句話說,不要讓其他線程在其他地方能夠看見一個構造期間的對象引用。不要指派給一個靜态字段,不要作為一個listener注冊給其他對象等等。這些操作應該在構造方法之後完成,而不是構造方法中來完成。

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;
    }
  }
}
           

上面的類展示了final字段應該如何使用。一個正在執行reader方法的線程保證看到f.x的值為3,因為它是final字段。它不保證看到f.y的值為4,因為f.y不是final字段。如果FinalFieldExample的構造方法像這樣:

public FinalFieldExample() { // bad!
  x = 3;
  y = 4;
  // bad construction - allowing this to escape
  global.obj = this;
}
           

那麼,從global.obj中讀取this的引用線程不會保證讀取到的x的值為3。

能夠看到字段的正确的構造值固然不錯,但是,如果字段本身就是一個引用,那麼,你還是希望你的代碼能夠看到引用所指向的這個對象(或者數組)的最新值。如果你的字段是final字段,那麼這是能夠保證的。是以,當一個final指針指向一個數組,你不需要擔心線程能夠看到引用的最新值卻看不到引用所指向的數組的最新值。重複一下,這兒的“正确的”的意思是“對象構造方法結尾的最新的值”而不是“最新可用的值”。

現在,在講了如上的這段之後,如果在一個線程構造了一個不可變對象之後(對象僅包含final字段),你希望保證這個對象被其他線程正确的檢視,你仍然需要使用同步才行。例如,沒有其他的方式可以保證不可變對象的引用将被第二個線程看到。使用final字段的程式應該仔細的調試,這需要深入而且仔細的了解并發在你的代碼中是如何被管理的。

如果你使用JNI來改變你的final字段,這方面的行為是沒有定義的。

原文

How do final fields work under the new JMM?

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.

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:

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.

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”.

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.

There is no defined behavior if you want to use JNI to change final fields.

原創文章,轉載請注明: 轉載自并發程式設計網 – ifeve.com本文連結位址: Java記憶體模型FAQ(九)在新的Java記憶體模型中,final字段是如何工作的