我有两个线程在公共变量上进行计算" n",一个线程增加" n"每一次,另一次减少" n"每次,当我在这个变量上没有使用volatile关键字时,我无法理解的事情发生了,请帮忙解释一下,该片段如下:
public class TwoThreads {
private static int n = 0;
private static int called = 0;
public static void main(String[] args) {
for (int i = 0; i < 1000; i++) {
n = 0;
called = 0;
TwoThreads two = new TwoThreads();
Inc inc = two.new Inc();
Dec dec = two.new Dec();
Thread t = new Thread(inc);
t.start();
t = new Thread(dec);
t.start();
while (called != 2) {
//System.out.println("----");
}
System.out.println(n);
}
}
private synchronized void inc() {
n++;
called++;
}
private synchronized void dec() {
n--;
called++;
}
class Inc implements Runnable {
@Override
public void run() {
inc();
}
}
class Dec implements Runnable {
@Override
public void run() {
dec();
}
}
}
1)我期待的是&#34; n = 0,称为= 2&#34;执行后,但很可能在while循环中可以阻止主线程;
2)但是当我取消注释这一行时,该程序按预期进行:
//System.out.println("----");
3)我知道我应该使用&#34; volatile&#34; on&#34;叫&#34;,但我无法解释上述原因;
4)&#34;叫&#34;是&#34;读取并加载&#34;在特定线程的工作记忆中,但为什么它没有&#34;存储和写入&#34;在&#34; long&#34;之后回到主线程while循环,如果不是,为什么一个简单的&#34; print&#34;线可以产生这样的差异
答案 0 :(得分:2)
您已经同步写入数据(在inc和dec中),但不能读取数据(在main中)。两者应该同步以获得可预测的效果。否则,主机永远不会“看到”由inc和dec完成的更改。
答案 1 :(得分:0)
你不知道确切地称为++将在哪里执行,你的主线程将继续生成新的线程,这将导致互斥,我的意思是每次只有一个线程可以调用++,因为方法是同步的,而你我不知道每一个都是正确的线程。可能会两次执行n ++或n--,你不知道这一点,当主线程达到你的条件时,可能会执行十次n ++。
并尝试阅读有关数据竞赛的信息
while (called != 2) {
//System.out.println("----");
}
//.. place for data race, n can be changed
System.out.println(n);
答案 2 :(得分:0)
您需要在此处同步called
的访问权限:
while (called != 2) {
//System.out.println("----");
}
我想添加getCalled
方法
private synchronized int getCalled() {
return called;
}
并将called != 2
替换为getCalled() != 2
如果您对发生此问题的原因感兴趣,可以阅读有关java内存模型上下文中的可见性的内容。