编程语言
首页 > 编程语言> > 用Java完全初始化不可变对象后,它们会被发布吗?

用Java完全初始化不可变对象后,它们会被发布吗?

作者:互联网

我正在阅读Fred Long撰写的Java Concurrency Guidelines中有关不可变对象和线程安全性的文章.

这是本书中的代码片段.

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

现在打开他们提到的第162页.这是另一个代码段.

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

}

随后是它自己的解释:

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.

那么,我大胆的两个说法是否相互矛盾?

A编写了一段代码来测试该案例,运行了几次,但从未得到0.只有null或42.这是我的代码:

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

}

解决方法:

这两个陈述并不矛盾.如果是第一个示例,则字段n是最终的.这就是使Helper类不可变的原因.在第二个示例中,n不是最终的,Helper不是不可变的,这意味着可以返回部分初始化的实例.

就您的代码而言,Peter是正确的,由于多种原因,很难在实践中进行测试.

标签:multithreading,concurrency,immutability,java
来源: https://codeday.me/bug/20191118/2032227.html