JAVA中线程间共享变量
我有两个线程对一个公共变量“n”进行计算,一个线程每次增加“n”,另一个线程每次减少“n”,当我没有在这个变量上使用volatile关键字时,发生了一些我无法理解的事情,sb那里请帮助解释,代码片段如下:JAVA中线程间共享变量,java,multithreading,variables,shared,volatile,Java,Multithreading,Variables,Shared,Volatile,我有两个线程对一个公共变量“n”进行计算,一个线程每次增加“n”,另一个线程每次减少“n”,当我没有在这个变量上使用volatile关键字时,发生了一些我无法理解的事情,sb那里请帮助解释,代码片段如下: public class TwoThreads { private static int n = 0; private static int called = 0; public static void main(String[] args) { for (int i = 0;
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();
}
}
3) 我知道我应该在“called”上使用“volatile”,但我无法解释为什么会发生上述情况
4) “called”是特定线程的工作内存中的“read and load”,但为什么它在“long”while循环之后不“store and write”返回到主线程中呢?如果不是,为什么一个简单的“print”行可以产生如此大的差异呢?您已经同步写入了数据(inc和dec),但没有读取数据(main)。两者都应该同步以获得可预测的效果。否则,main可能永远不会“看到”inc和dec所做的更改。您已同步写入数据(inc和dec),但未同步读取数据(main)。两者都应该同步以获得可预测的效果。否则,main可能永远不会“看到”inc和dec所做的更改。您不知道调用的++将在何处执行,您的主线程将继续生成新线程,这些线程将进行互斥,我的意思是每次只有一个线程可以调用+,因为方法是同步的,你不知道每一根线都会是它。可能是两次将执行n++或n-,您不知道这一点,可能是十次将执行n++而主线程达到您的条件 并尝试阅读有关数据竞争的内容
while (called != 2) {
//System.out.println("----");
}
//.. place for data race, n can be changed
System.out.println(n);
你不知道被调用的++将在哪里执行,你的主线程将继续产生新的线程,这将产生互斥,我的意思是每次只有一个线程可以被调用+,因为方法是同步的,你不知道每个线程将被调用。可能是两次将执行n++或n-,您不知道这一点,可能是十次将执行n++而主线程达到您的条件 并尝试阅读有关数据竞争的内容
while (called != 2) {
//System.out.println("----");
}
//.. place for data race, n can be changed
System.out.println(n);
您需要在此处同步访问名为的
:
while (called != 2) {
//System.out.println("----");
}
我建议添加getCalled
方法
private synchronized int getCalled() {
return called;
}
并替换调用的2带有getCalled()!=2
如果您对出现此问题的原因感兴趣,可以阅读java内存模型上下文中的可见性。您需要同步对的访问,该称为
while (called != 2) {
//System.out.println("----");
}
我建议添加getCalled
方法
private synchronized int getCalled() {
return called;
}
并替换调用的2带有getCalled()!=2
如果您对出现此问题的原因感兴趣,可以阅读java内存模型上下文中的可见性。我认为使用Thread.sleep()方法而不是print()可以获得相同的效果。@victor您是对的,任何行都可以具有相同的效果,无法解释为什么,您无法控制任何线程何时执行;但是main将继续执行,当它完成时,另一个线程也将继续执行。这就是为什么应该使用Thread.join()停止main,直到线程的其余部分完成。(Thread.sleep()会做一段时间,但是join()会等到他们是芬兰语)我认为你可以使用Thread.sleep()方法而不是print()来获得相同的效果。@victor你是对的,任何一行都可以有相同的效果,不能解释为什么,你不能控制任何线程何时执行;但是main将继续执行,当它完成时,另一个线程也将继续执行。这就是为什么应该使用Thread.join()停止main,直到线程的其余部分完成。(Thread.sleep()将执行一段时间,但join()将等待,直到他们使用芬兰语)谢谢serega,我希望除了主线程之外,最多会有两个线程(inc和dec)在运行,因为否则它可能会在while循环中被阻塞,我循环1000次只是为了确定它是否有被阻塞的可能性。你不能确定while循环是否会阻塞,为此请使用called>2,因为当主线程到达while循环时,你的线程可以执行called++1000次,而你也不能确定,因为主线程可以在inc和dec线程调用++之前到达while循环,并且条件将失败,并且您将获得另一对新线程hanks serega,我预计除了主线程之外,最多会有两个线程(inc和dec)在运行,因为否则它可能会在while循环中被阻塞,我循环1000次只是为了确定它是否有被阻塞的可能性。你不能确定while循环是否会阻塞,为此请使用called>2,因为当主线程到达while循环时,你的线程可以执行called++1000次,而你也不能确定,因为主线程可以在inc和dec线程调用++之前到达while循环,并且条件将失败,您将获得另一对新线程。我需要查看您的整个代码,并且有很多不同的方法来实现这一点。基本上,将读写代码放在同步块中,并使用公共锁对象。因为main是静态方法,所以如果您想在那里阅读,您只能使用类对象或静态类级别变量。例如:synchronized(TwoThreads.class){
/read n和/或called here
//write n和/或called here
}
我需要查看您的整个代码,还有很多不同的方法。基本上,将读写代码放在同步块中,并使用公共锁对象。因为main是静态方法,所以如果您想在那里阅读,您只能使用类对象或静态类级别变量。例如:synchronized(TwoThreads.class){
/read n和/或called here
/write n和/或called here
}
talex谢谢,我会用谷歌搜索一下,但是为什么“System.out.println(----”;)可以得到这个任务呢