以下是我的以下代码:
class Test {
private int x = 0;
public void incX() {
synchronized(this) {
++x;
}
System.out.println("x is: "+x+" "+Thread.currentThread().getName());
}
public static void main(String[] args) {
Test test = new Test();
Thread t1 = new Thread(() -> {
test.incX();
});
Thread t2 = new Thread(() -> {
test.incX();
});
t1.start();
t2.start();
System.out.println("Done");
}
}
这是我的输出:
x is: 2 Thread-1
x is: 1 Thread-0
此处线程t2
输出2,但线程t1
也应输出2,对吧?当x
等于2时,它应该对线程t1
可见,对吧?那么为什么线程t1
是1?
线程t2
输出2然后线程t1
输出1怎么可能?由于线程t2
已输出2,因此x的值应为2.那么线程t1
如何仍能输出1?我误解了吗?
答案 0 :(得分:3)
您的整个System.out.println
行与原子相距甚远
例如,在字符串的构造和System.out.println
的调用之间,可能会发生很多事情。
让我们考虑一个等效的代码块:
public void incX() {
synchronized(this) {
++x;
}
String implicit = "x is: " + x + " " + Thread.currentThread().getName();
// <-- "Point X"
System.out.println(implicit);
}
现在情况可以是这样的:
x是:1 Thread-0
x是:2 Thread-1
System.out.println
,使得总输出到目前为止:
x是:2 Thread-1
System.out.println
,进行总体输出:
x是:2 Thread-1 x是:1 Thread-0
将x
的值复制到synchronized
块中的另一个变量应足以使行为符合您的期望:
public void incX() {
int val;
synchronized(this) {
val = ++x;
}
System.out.println("x is: " + val + " " + Thread.currentThread().getName());
}
答案 1 :(得分:3)
首先,您的代码未正确同步。你的表达
"x is: "+x+" "+Thread.currentThread().getName()
在没有同步的情况下读取共享变量x
的值,即在数据争用中。但是,它至少会看到前面的synchronized
块观察到的值。
现在让我们考虑您的计划的可能输出。线程逐步进行,并且所有线程的步骤都是交错的。请考虑以下顺序:
Thread-0
输入synchronized
块,读取x == 0
,将其更新为1
,然后离开该块。后续字符串表达式读取此值1
。Thread-1
输入synchronized
块,读取x == 1
,将其更新为2
,然后离开该块。后续字符串表达式读取此值2
。Thread-1
输入synchronized
方法println
并输出结果2
。Thread-0
输入synchronized
方法println
并输出结果1.