在 JAVA 中的线程之间共享变量
Sharing variable between threads in JAVA
当我没有在这个变量,发生了一些我无法理解的事情,请某人帮忙解释一下,代码片段如下:
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) 我期望的是执行后 "n=0,called=2",但主线程可能会在 while 循环中被阻塞;
2) 但是当我取消注释这一行时,程序如预期的那样:
//System.out.println("----");
3) 我知道我应该在 "called" 上使用 "volatile",但我无法解释为什么会出现上述情况;
4) "called"在特定线程的工作内存中是"read and load",但是为什么在"long"while循环之后它没有"store and write"回到主线程,如果不是,为什么简单的 "print" 行可以产生如此大的差异
您已同步写入数据(在 inc 和 dec 中),但未同步读取数据(在 main 中)。两者都应该同步以获得可预测的效果。否则,main 可能永远不会 "sees" inc 和 dec.
所做的更改
你不知道called++到底会在哪里执行,你的主线程会不断的产生新的线程,这会造成互斥,我的意思是每次只有一个线程可以make called++,因为方法是同步的,而你不知道每个线程都会是它。可能会执行两次n++或n--,你不知道这个,可能会执行十次n++,而主线程达到你的条件。
并尝试阅读有关数据竞争的内容
while (called != 2) {
//System.out.println("----");
}
//.. place for data race, n can be changed
System.out.println(n);
您需要在此处同步对 called
的访问:
while (called != 2) {
//System.out.println("----");
}
我建议添加 getCalled
方法
private synchronized int getCalled() {
return called;
}
并将called != 2
替换为getCalled() != 2
如果您对为什么会出现此问题感兴趣,可以阅读 java 内存模型上下文中的可见性。
当我没有在这个变量,发生了一些我无法理解的事情,请某人帮忙解释一下,代码片段如下:
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) 我期望的是执行后 "n=0,called=2",但主线程可能会在 while 循环中被阻塞;
2) 但是当我取消注释这一行时,程序如预期的那样:
//System.out.println("----");
3) 我知道我应该在 "called" 上使用 "volatile",但我无法解释为什么会出现上述情况;
4) "called"在特定线程的工作内存中是"read and load",但是为什么在"long"while循环之后它没有"store and write"回到主线程,如果不是,为什么简单的 "print" 行可以产生如此大的差异
您已同步写入数据(在 inc 和 dec 中),但未同步读取数据(在 main 中)。两者都应该同步以获得可预测的效果。否则,main 可能永远不会 "sees" inc 和 dec.
所做的更改你不知道called++到底会在哪里执行,你的主线程会不断的产生新的线程,这会造成互斥,我的意思是每次只有一个线程可以make called++,因为方法是同步的,而你不知道每个线程都会是它。可能会执行两次n++或n--,你不知道这个,可能会执行十次n++,而主线程达到你的条件。
并尝试阅读有关数据竞争的内容
while (called != 2) {
//System.out.println("----");
}
//.. place for data race, n can be changed
System.out.println(n);
您需要在此处同步对 called
的访问:
while (called != 2) {
//System.out.println("----");
}
我建议添加 getCalled
方法
private synchronized int getCalled() {
return called;
}
并将called != 2
替换为getCalled() != 2
如果您对为什么会出现此问题感兴趣,可以阅读 java 内存模型上下文中的可见性。