final List<T> 和线程安全

final List<T> and thread safety

我正在尝试了解 final 字段在多线程环境中的作用。

我阅读了这些相关帖子:

final fields and thread-safety

Java concurrency: is final field (initialized in constructor) thread-safe?

Java: Final field freeze on object reachable from final fields

并尝试模拟 final 字段将阻止线程处理未完全构造的对象的情况。

public class Main {
    public static void main(String[] args) {
        new _Thread().start();
        new Dummy();
    }

    static class _Thread extends Thread {
        static Dummy dummy;

        @Override
        public void run() {
            System.out.println(dummy.getIntegers().size() == 10_000);
        }
    }

    static class Dummy {
        private final List<Integer> integers = new ArrayList<>();

        public Dummy() {
            _Thread.dummy = this;

            for (int a = 0; a < 10_000; a++) {
                integers.add(a);
            }
        }

        public List<Integer> getIntegers() {
            return integers;
        }
    }
}

据我了解,_Thread 将在 getIntegers() 停止执行并等待循环完成填充集合。但是字段integers是否有final修饰符,run()的结果是不可预测的。我也知道有NPE的可能。

final在这里没有区别。该代码不是线程安全的,无论 final 是否存在。

这不是线程安全的有两个原因。

  1. 您正在发布(并可能改变)Dummy 在其构造函数完成之前的状态。无论变量是否为 final.

    ,这都是不安全的
  2. 您在 getIntegers() 调用中 return 共享可变对象。所以这意味着调用者 可以 更改它,并且第二个调用者可能会或可能不会看到结果......由于缺乏同步。再一次 final 对此没有影响。


final 的线程安全保证是有限的。 JLS 是这样说的:

final fields also allow programmers to implement thread-safe immutable objects without synchronization. A thread-safe immutable object is seen as immutable by all threads, even if a data race is used to pass references to the immutable object between threads. This can provide safety guarantees against misuse of an immutable class by incorrect or malicious code. final fields must be used correctly to provide a guarantee of immutability.

An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object's final fields.

要点是 final 保证仅适用于 不可变的 对象,并且它们仅适用于 after return 对象的构造函数。

在您的示例中,不满足这些先决条件中的任何一个。因此,保证不适用。