java 中多线程的意外答案

unexpected answers in multithreading in java

这是我的代码,使用 4 个线程将变量 'res' 乘以 4*10^7 次:

class MathSin extends Thread {

    public double a;

    public MathSin(int degree) {
        a = degree;
    }

    @Override
    public void run() {

        for (int i = 0; i < Math.pow(10., 7); i++)
            MathThreads.res++;

    }
}

class MathThreads {
    public static double res = 0;

    public static void main(String args[]) {
        MathSin st = new MathSin(8);
        MathSin ct = new MathSin(8);
        MathSin tt = new MathSin(8);
        MathSin qt = new MathSin(8);
        st.start();
        ct.start();
        tt.start();
        qt.start();
        try { // wait for completion of all thread and then sum
            st.join();
            ct.join(); // wait for completion of MathCos object
            tt.join();
            qt.join();
            System.out.println(res);
        } catch (InterruptedException IntExp) {
        }
    }
}

这些是一些答案:

1.8499044E7

2.3446789E7
.
.
.

我希望得到 3.0E7 但得到了另一个不同的答案。

如何解决这个问题?

有什么问题?

您在更新 static 变量时观察到竞态条件 res

MathThreads.res++

相当于:

double tmp = MathThreads.res;
MathThreads.res = tmp + 1;

如果两个线程同时读取 tmp 的值,并且都用 tmp + 1 更新 res 会发生什么?好吧,只是忘记了一个增量:res 结束为 tmp + 1 而不是 tmp + 1 + 1!

因此,当 4 个线程同时更新 res 时,您只会以未定义的行为结束:由于这些竞争条件,无法预测 res 的最终值。两次执行相同的代码会得到不同的答案。

如何解决这个问题?

为了让你的代码线程安全,你需要为res使用线程安全的结构:一个可以并发更新和访问的结构。

在你的情况下,AtomicLong 似乎是完美的选择:

public static AtomicLong res = new AtomicLong(0);

并且在运行方法中:

for (int i = 0; i < Math.pow(10., 7); i++) {
    MathThreads.res.incrementAndGet();
}