1

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

初始状态:线程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 中,有时字符串 cs 的值实际上为空。但我希望它具有“成功”的价值。另一方面,如果我将 Thread.sleep(sometime) 放在断言语句之前,那么我可以看到 cs 的预期值,我不知道它为什么会发生。

我试图将归档变量 s 声明为 volatile,但它没有帮助。

谢谢!

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

4

4 回答 4

3

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

因此,如果您在没有调用sleepwait在其中任何一个线程上运行两个线程(taht 没有interrupting其中任何一个),那么 CPU 分配将在两个Threads. Thread B因此,甚至在完成之前获得 CPU肯定是有可能的Thread A

但是,当您Thread B的睡眠时间足以A完成工作时,将CPU分配给线程 A 直到那时。所以它有足够的时间来完成它的工作(假设你只有两个线程A并且B当时处于runnable状态)

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

为确保您Thread A在之前完成Thread B,您可以调用wait您正在使用Thread B的特定实例Thread A。然后当Thread A完成时,它会调用notify通知Thread B它已经完成工作,然后Thread B可以继续,

于 2012-10-26T05:26:33.533 回答
3

您需要在线程 A 和 B 之间进行一些同步。

线程 A:

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

线程 B:

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

如果没有这种同步(或类似的东西,比如一个synchronized块),你不能保证写入已经从一个线程到另一个线程(根本,或者以任何特定的顺序)。

于 2012-10-26T05:28:34.547 回答
1

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

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

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

试试看!

于 2012-10-26T12:02:00.413 回答
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"));

如果C对象是由 ThreadA 构造的,那么可以改为Object lock = new Object();为 A 和 B 定义一个 final 来同步。您永远不应该对正在更改的对象进行同步,因为 A 和 B必须同一个对象引用上同步,等待/通知才能工作。

我试图将归档变量 s 声明为 volatile,但它没有帮助。

如果两者都不稳定c.i c.s那么这应该可以工作,但这将取决于 A 和 B 之间的信号发送方式。可能是B太早醒来了?此外,如果c对象正在更改,那么您需要使C c自身的定义也不稳定。这里的synchronized关键字处理信令和内存同步。

希望这可以帮助。

于 2012-10-26T11:46:00.773 回答