不可变对象如何帮助防止竞争条件

How immutable objects help to prevent race conditions

以下是关于如何防止竞争条件的答案。 What is a race condition?

The best thing would be to create side-effect free and stateless functions, use immutables as much as possible. But that is not always possible. So using java.util.concurrent.atomic, concurrent data structures, proper synchronisation, and actor based concurrency will help.

这个答案说尽可能多地使用不可变。我对不可变对象如何防止竞争条件感到困惑。

仅当允许至少一个线程写入/更改实例状态时才会出现竞争条件。 不可变实例是只读,它们的状态不能改变,因此所有线程只读对象并查看 相同的值

当 "two or more threads ... access the same memory location concurrently, and at least one of the accesses is for writing, and the threads are not using any exclusive locks to control their accesses to that memory."

时出现 data race

如果数据不可变,就不会有数据竞争,因为不可能有写访问。

此外,Java Memory Model guarantees:

once an object is constructed, the values assigned to the final fields in the constructor will be visible to all other threads without synchronization.

当计算结果取决于表达式和语句的计算顺序时,就会出现竞争条件。

如果表达式和语句的计算改变状态,产生副作用,结果可能不同。

如果代码中的所有内容都是不可变的,那么在计算表达式和语句时就不会改变状态,也不会产生副作用。因此评估的顺序不会影响最终结果。

考虑以下代码:

Map<String, Integer> map = Collections.singletonMap("key", 0);

public void increment() {
    int val = map.get("key);
    map.put("key", val + 1);
}

如果两个线程同时执行方法 increment() 的每条语句,它们都读取相同的值 0 并且都将相同的增量值 1 放入 map .因此结果将是 1.

如果两个线程(碰巧)连续执行所有语句,一个线程将读取值 0 并放入值 1,而另一个线程将读取值 1并将值 2.

现在如果映射是不可变的并且两个线程都将执行以下方法:

public void logMap() {
    System.out.println("Key has value " + map.get("key"));
}

结果总是一样的,因为没有影响计算的副作用(除了 System.out.println 所做的更改)。