【问题标题】:Sharing variable between threads in JAVAJAVA线程间共享变量
【发布时间】:2016-08-30 12:34:01
【问题描述】:

我有两个线程对一个公共变量“n”进行计算,一个线程每次增加“n”,另一个线程每次减少“n”,当我不在这个变量上使用 volatile 关键字时,会发生我无法理解的事情, sb 那里请帮忙解释一下,sn-p 如下:

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) 我知道我应该在“被调用”上使用“volatile”,但我无法解释为什么会发生上述情况;

4)“调用”是在特定线程的工作内存中“读取和加载”,但是为什么在“长”while循环之后它不是“存储和写入”回主线程,如果不是,为什么简单的“打印” " 行可以产生如此大的影响

【问题讨论】:

  • 我认为你可以使用 Thread.sleep() 方法而不是 print() 来获得相同的效果。
  • @victor 你是对的,任何一行都可以有同样的效果,无法解释为什么
  • 好吧,你无法控制何时执行任何线程;但是主线程将继续执行,当它完成时,另一个线程也将继续执行。这就是为什么你应该使用 Thread.join() 来停止主线程,直到线程的其余部分可以完成。 (Thread.sleep() 会做一段时间,但 join() 会等到他们完成)

标签: java multithreading variables shared volatile


【解决方案1】:

您已同步写入数据(在 inc 和 dec 中),但没有读取数据(在 main 中)。两者都应该同步以获得可预测的效果。否则,main 可能永远不会“看到” inc 和 dec 所做的更改。

【讨论】:

  • 我需要查看您的整个代码,并且为此有很多不同的方法。基本上,将您的读写代码放在同步块中,并使用一个通用的锁对象。由于 main 是静态方法,如果你想在那里阅读,你只能使用类对象或静态类级变量。例如:synchronized(TwoThreads.class) {//read n and/or called here}synchronized(TwoThreads.class) {//write n and/or called here}
【解决方案2】:

你不知道究竟call++会在哪里执行,你的主线程会不断地产生新的线程,这会产生互斥,我的意思是每次只有一个线程可以调用call++,因为方法是同步的,而你没有'不知道每个确切的线程都会是它。可能两次执行n++或n--,你不知道,可能十次执行n++,而主线程达到你的条件。

并尝试阅读有关数据竞争的信息

 while (called != 2) {
            //System.out.println("----");
 }

//.. place for data race, n can be changed

 System.out.println(n);

【讨论】:

  • 感谢 serega,我希望除了主线程之外最多有两个线程(inc 和 dec)在运行,因为否则它可能会被阻塞在 while 循环中,我循环 1000 次只是为了看到那里有可能会卡住。
  • 您不能确定 while 循环是否会阻塞,为此请使用 called > 2,因为您的线程可以在您的主线程到达 while 循环时调用 ++ 1000 次)而您不能无论如何都要确定,因为主线程可以在 inc 和 dec 线程调用 ++ 之前到达 while 循环,并且条件将失败,您将获得另一对新线程
【解决方案3】:

您需要在此处同步访问called

while (called != 2) {
    //System.out.println("----");
}

我建议添加getCalled方法

private synchronized int getCalled() {
    return called;
}

并将called != 2 替换为getCalled() != 2

如果您对为什么会出现此问题感兴趣,可以阅读有关 java 内存模型上下文中的可见性。

【讨论】:

  • talex 谢谢,我会用谷歌搜索,但为什么是 "System.out.println("----");"可以完成工作,以及何时将数据同步回来......
  • 因为它内部有同步并且这个同步会刷新一些缓存。不保证将来不会更改。
猜你喜欢
  • 2018-08-28
  • 2019-05-01
  • 2023-03-04
  • 2019-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-22
相关资源
最近更新 更多