Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Testing initialization safety of final fields

I am trying to simply test out the initialization safety of final fields as guaranteed by the JLS. It is for a paper I'm writing. However, I am unable to get it to 'fail' based on my current code. Can someone tell me what I'm doing wrong, or if this is just something I have to run over and over again and then see a failure with some unlucky timing?

Here is my code:

public class TestClass {      final int x;     int y;     static TestClass f;      public TestClass() {         x = 3;         y = 4;     }      static void writer() {         TestClass.f = new TestClass();     }      static void reader() {         if (TestClass.f != null) {             int i = TestClass.f.x; // guaranteed to see 3             int j = TestClass.f.y; // could see 0              System.out.println("i = " + i);             System.out.println("j = " + j);         }     } } 

and my threads are calling it like this:

public class TestClient {      public static void main(String[] args) {          for (int i = 0; i < 10000; i++) {             Thread writer = new Thread(new Runnable() {                 @Override                 public void run() {                     TestClass.writer();                 }             });              writer.start();         }          for (int i = 0; i < 10000; i++) {             Thread reader = new Thread(new Runnable() {                 @Override                 public void run() {                     TestClass.reader();                 }             });              reader.start();         }     } } 

I have run this scenario many, many times. My current loops are spawning 10,000 threads, but I've done with this 1000, 100000, and even a million. Still no failure. I always see 3 and 4 for both values. How can I get this to fail?

like image 331
sma Avatar asked Feb 21 '11 14:02

sma


People also ask

How do you initialize a final field in Java?

Note that any final field must be initialized before the constructor completes. For static final fields, this means that we can initialize them: upon declaration as shown in the above example. in the static initializer block.

Where can a final constant instance field be initialized?

Within a default constructor.

Is final thread safe Java?

1) Immutable objects are by default thread-safe because their state can not be modified once created. Since String is immutable in Java, it's inherently thread-safe. 2) Read-only or final variables in Java are also thread-safe in Java.

When should a field be final Java?

When should I use final? One answer to this is "whenever you possibly can". Any field that you never expect to be changed (be that a primitive value, or a reference to an object, whether or not that particular object is itself immutable or not), should generally be declared final.


2 Answers

I wrote the spec. The TL; DR version of this answer is that just because it may see 0 for y, that doesn't mean it is guaranteed to see 0 for y.

In this case, the final field spec guarantees that you will see 3 for x, as you point out. Think of the writer thread as having 4 instructions:

r1 = <create a new TestClass instance> r1.x = 3; r1.y = 4; f = r1; 

The reason you might not see 3 for x is if the compiler reordered this code:

r1 = <create a new TestClass instance> f = r1; r1.x = 3; r1.y = 4; 

The way the guarantee for final fields is usually implemented in practice is to ensure that the constructor finishes before any subsequent program actions take place. Imagine someone erected a big barrier between r1.y = 4 and f = r1. So, in practice, if you have any final fields for an object, you are likely to get visibility for all of them.

Now, in theory, someone could write a compiler that isn't implemented that way. In fact, many people have often talked about testing code by writing the most malicious compiler possible. This is particularly common among the C++ people, who have lots and lots of undefined corners of their language that can lead to terrible bugs.

like image 128
Jeremy Manson Avatar answered Sep 19 '22 14:09

Jeremy Manson


From Java 5.0, you are guarenteed that all threads will see the final state set by the constructor.

If you want to see this fail, you could try an older JVM like 1.3.

I wouldn't print out every test, I would only print out the failures. You could get one failure in a million but miss it. But if you only print failures, they should be easy to spot.

A simpler way to see this fail is to add to the writer.

f.y = 5; 

and test for

int y = TestClass.f.y; // could see 0, 4 or 5 if (y != 5)     System.out.println("y = " + y); 
like image 32
Peter Lawrey Avatar answered Sep 18 '22 14:09

Peter Lawrey