Maksim Dmitriev
Maksim Dmitriev

Reputation: 6209

Are immutable objects published after they are fully initialized in Java?

I am reading about immutable objects and thread safety in Java Concurrency Guidelines by Fred Long.

Here is a code snippet from the book.

// Immutable Helper
public final class Helper {
    private final int n;

    public Helper(int n) {
        this.n = n;
    }
    // ...
}

// and a mutable Foo class:
final class Foo {
    private Helper helper;

    public Helper getHelper() {
        return helper;
    }

    public void setHelper(int num) {
        helper = new Helper(num);
    }
}

The code snippet is followed by the explanation:

The getHelper() method publishes the mutable helper field. Because the Helper class is immutable, it cannot be changed after it is initialized. Furthermore, because Helper is immutable, it is always constructed properly before its reference is made visible in compliance with guideline “TSM03-J. Do not publish partially initialized objects” on page 162.

Now let's open page 162 they've mentioned. Here is another code snippet.

class Foo {
    private Helper helper;

    public Helper getHelper() {
        return helper;
    }

    public void initialize() {
        helper = new Helper(42);
    }
}

public class Helper {
    private int n;

    public Helper(int n) {
        this.n = n;
    }

}

It's followed by its own explanation:

If a thread accesses helper using the getHelper() method before the initialize() method has been called, the thread will observe an uninitialized helper field. Later, if one thread calls initialize() and another calls getHelper(), the second thread might observe one of the following:

  • the helper reference as NULL,
  • a fully initialized Helper object with the n field set to 42,
  • a partially initialized Helper object with an uninitialized n that contains the default value 0.

So don't the two statements I made bold, contradict each other?


A wrote a piece of code to test the case, ran it a few times, and never got 0. Only null or 42. Here is my code:

package com.sample;

public class Main {

    public static void main(String[] args) {
        for (int i = 0; i < 20; i++) {
            Foo foo = new Foo();
            Initer initer = new Initer(foo);
            Getter getter = new Getter(foo);
            initer.start();
            getter.start();
        }
    }

    static class Getter extends Thread {

        private Foo foo;

        Getter(Foo foo) {
            this.foo = foo;
        }

        @Override
        public void run() {
            System.out.println(foo.getHelper());
        }
    }

    static class Initer extends Thread {

        private Foo foo;

        Initer(Foo foo) {
            this.foo = foo;
        }

        @Override
        public void run() {
            foo.initialize();
        }
    }

    static class Foo {
        private Helper helper;

        public Helper getHelper() {
            return helper;
        }

        public void initialize() {
            helper = new Helper(42);
        }
    }

    public static class Helper {
        private int n;

        public Helper(int n) {
            this.n = n;
        }

        @Override
        public String toString() {
            return Integer.toString(n);
        }
    }

}

Upvotes: 3

Views: 109

Answers (2)

Martin Serrano
Martin Serrano

Reputation: 3795

The two statements are not inconsistent. If the first example the field n is final. This is what makes the Helper class immutable. In the second example, n is not final, Helper is not immutable, which means a partially initialized instance could be returned.

As far as observing this with your code, Peter is right, it is difficult to test in practice for several reasons.

Upvotes: 2

Peter Lawrey
Peter Lawrey

Reputation: 533870

Unless the field is final there is no guarantee the value will be visible however,

  • there is no guarantee that it won't.
  • you are more likely to see this problem once the code has warmed up i.e. well after 10,000 iterations.
  • you are using println which has it's own memory barriers which will make it harder to see any inconsistency.

Upvotes: 1

Related Questions