由不同线程更新的对象仅对另一个线程部分可见

时间:2012-10-26 05:19:45

标签: java string multithreading share

我正在进行多线程编程并遇到有线问题:对象更新仅对另一个线程部分可见。这是伪代码:

初始状态:线程A正在运行,线程B被阻止

Class C {
    public int i;
    public String s;
}

主题A:

...
// c is an object of class C and is accessible by both thread A and B
c.i = 10;
c.s = "success";
wakeup thread B:

主题B:

// after wakeup
assert(c.i == 10);
assert(c.s.equals("success"));

问题是:在线程B中,有时字符串c.s的值实际上是null。但我希望它具有“成功”的价值。另一方面,如果我在断言语句之前放置Thread.sleep(sometime),那么我可以看到c.s的期望值。我不知道为什么会这样。

我试图将变量s声明为volatile,但它没有用。

谢谢!

更新 感谢所有的回复/答案。经过更多的实验和调查,我相信这是我正在使用的框架的一个错误。该框架管理线程的挂起/恢复,当有许多并发请求/连接(如10k线程)时,它会遇到麻烦。

4 个答案:

答案 0 :(得分:2)

当您使用多线程,然后所有线程,在其中共享您的CPU。它不确定哪个线程将在之前完成以及哪个线程将最后完成。最后开始的Thread可能最先结束。 这完全取决于所使用的CPU调度算法。

所以,如果你运行两个线程而没有在其中任何一个上调用sleepwait(taht没有任何interrupting),那么CPU分配将在两个线程之间切换Threads。因此,在Thread B完成之前,Thread A肯定有可能获得CPU。

但是,当Thread B处于休眠状态的时间足以让A完成作业时,CPU将被分配给线程A,直到那时为止。所以它有足够的时间来完成它的工作(前提是你只有两个线程AB当时处于runnable状态。

这就是为什么multithreading代码的结果永远不会相同的原因。它在多次运行中不断变化。因此,当您运行代码10 - 15次时。你可以看到这种差异。

为确保您的Thread AThread B之前完成,您可以在wait Thread B正在使用{1}}的特定实例中致电Thread A。然后,当Thread A完成后,它会致电notify通知Thread B它已完成作业,然后Thread B可以继续,

答案 1 :(得分:2)

线程A和B之间需要一些同步。

主题A:

c.i = 10;
c.s = "success";
c.notify()

主题B:

c.wait()
assert(c.i == 10);
assert(c.s.equals("success"));

如果没有这种同步(或类似的东西,如synchronized块),则无法保证写入从一个线程到另一个线程(完全或以任何特定顺序)。

答案 2 :(得分:1)

在多线程应用程序中,每个CPU都有一个本地内存缓存,并且内存操作会进行大量重新排序以进行优化。您需要确保线程之间共享的C实例为synchronized。这既确保了一个且只有一个线程可以进行操作,它确保两个线程都使用该对象的最新内存版本。

如果您使用的是synchronized关键字,则通常会在final对象上进行同步:

  // shared by both threads
  final C c = new C();
  ...

主题A:

  ...
  // this allows us to notify on c _and_ synchronizes memory
  synchronized (c) {
     c.i = 10;
     c.s = "success";
     // signal the other thread that is wait-ing
     c.notify();
  }

主题B:

  // this allows us to wait for C _and_ synchronizes memory
  synchronized (c) {
     // it's common to test for wait in while loop cause of "spurious interrupts"
     while (c.s == null) {
        // wait for c to be updated
        c.wait();
     }
  }
  assert(c.i == 10);
  assert(c.s.equals("success"));

如果ThreadA正在构造C对象,则可以为A和B定义最终Object lock = new Object();以进行同步。永远不要在正在更改的对象上进行同步,因为A和B 必须相同的对象引用上进行同步才能使wait / notify工作。

  

我试图将变量s声明为volatile,但它没有用。

如果c.i c.s都处于易失性状态,则此 应该工作,但这取决于A和B之间的信令传输方式可能是B太早醒来了?此外,如果c对象正在发生变化,那么您需要使C c本身的定义也不稳定。这里的synchronized关键字处理信令和内存同步。

希望这有帮助。

答案 3 :(得分:1)

使用volatile会解决您的问题,例如在您的情况下,您可以:

class C {
    public int i;
    volatile public String s;
}

声明volatile变量意味着: 这个变量的值永远不会被线程本地缓存:所有读写都将直接进入“主存”; 对变量的访问就好像它被包含在同步块中一样,自身同步。

试一试!