使这个类线程安全

时间:2017-01-02 09:51:36

标签: java multithreading thread-safety

我刚刚参加考试,我从之前的考试题目中得到以下非线程安全代码:

class Q3 {
    private boolean f[] = new boolean[20];

    public Q3() { 
        for (int j = 0; j < 20; j++) {
            f[j] = true;
        }
    }

    public void negate(int a, int b) {
        for(int j = a; j < b; j++) {
            f[j] = !f[j];
        }
    }

    public void swap(int a, int b) {
        boolean temp = f[a];
        f[a] = f[b];
        f[b] = temp;
    }
}

通过进行以下调整,我确保了线程安全:

class Q3 {
    private boolean f[] = new boolean[20];

    public Q3() {
        for (int j = 0; j < 20; j++) {
            f[j] = true;
        }
    }

    synchronized void negate(int a, int b) {
        for (int j = a; j < b; j++) {
            f[j] = !f[j];
        }
    }

    synchronized void swap(int a, int b) {
        boolean temp = f[a];
        f[a] = f[b];
        f[b] = temp;
    }
}

但有人可以向我解释为什么原始代码不是线程安全的吗?我知道如何使代码线程安全,但我仍然不确定为什么代码被定义为线程安全

2 个答案:

答案 0 :(得分:2)

这个答案假设多个线程可能在Q3个对象的同一个实例上运行。以swap()方法为例:

public void swap(int a, int b) {
    boolean temp = f[a];
    f[a] = f[b];
    f[b] = temp;
}

如果两个单独的线程同时调用swap()并进行交错,则可能发生以下情况:

Thread one:
boolean temp = f[a];
f[a] = f[b];

Thread two:
boolean temp = f[a];   // his temp = f[b], wrong!

Thread one:
f[b] = temp;           // f[b] = f[b], no swap!

Thread two:
f[a] = f[b];
f[b] = temp;           // f[a] = f[b], wrong

最终结果是f[a]f[b]都以后者的值结束,对于两个线程,这显然不是你想要的。通过创建swap()方法synchronized,您可以确保给定的线程将执行该方法原子,即给定的线程将单独完成整个方法,或者不会,两者之间没有任何东西。

答案 1 :(得分:1)

让我们说2个线程同时分别调用negate和swap,并且所有索引最初都为true。现在让我们说两个线程同时在第四个索引中读取value = true。第1个线程调用否定,它否定第4个元素使其为假,同时第2个线程读取第4个元素并交换为第5个元素,这是真的。因此,在这种情况下的最终结果将是第4个索引值= false第5个索引值= true。

现在,由于我们无法预测执行顺序,因此它们可能是另一种执行方案。 线程1读取第4个元素使其为false然后线程2在线程1更新后读取值,因此将其读取为false并将此值与第5个元素交换。所以在这种情况下,最终结果将是4th Index value = true和5th index = false 所以,底线,如果我们不使它同步,我们无法预测线程执行顺序,可能会得到意想不到的结果。因此,当你说同步时,这两个方法永远不会被一起调用,因此不可能通过多个线程同时读/写相同的索引元素。