java - 不可变对象(immutable对象)在 Java 中完全初始化后是否发布?

标签 java multithreading concurrency immutability

我正在阅读 Fred Long 撰写的 Java 并发指南 中有关不可变对象(immutable对象)和线程安全的内容。

这是书中的一段代码。

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

}

最佳答案

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

就用您的代码观察这一点而言,Peter 是对的,由于多种原因很难在实践中进行测试。

关于java - 不可变对象(immutable对象)在 Java 中完全初始化后是否发布?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35875055/

相关文章:

java - 在存储的 CLOB 中包含自动增量值

Java 执行器 : Small tasks or big ones?

java - 启动线程时出现非法线程状态异常

java - LMAX Disruptor 如何解决典型的消息代理问题?

java - 贾克斯RS : How can I run a method automatically everytime my server restarts?

java - Web应用程序中的并发控制

java - 包含相同初始数组的副本的对象。一个数组中的值更改会导致所有其他数组中的值更改。我怎样才能防止这种情况发生?

java并发与scheduledexecutor和无限循环

java - 异步运行方法并能够查看其状态的最简单方法是什么?

java - 如何知道我的 threadPoolExecutor 中哪个特定的可运行程序已死亡?