我刚刚参加考试,我从之前的考试题目中得到以下非线程安全代码:
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;
}
}
但有人可以向我解释为什么原始代码不是线程安全的吗?我知道如何使代码线程安全,但我仍然不确定为什么代码被定义为线程安全
答案 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 所以,底线,如果我们不使它同步,我们无法预测线程执行顺序,可能会得到意想不到的结果。因此,当你说同步时,这两个方法永远不会被一起调用,因此不可能通过多个线程同时读/写相同的索引元素。