我正在进行多线程编程并遇到有线问题:对象更新仅对另一个线程部分可见。这是伪代码:
初始状态:线程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线程)时,它会遇到麻烦。
答案 0 :(得分:2)
当您使用多线程,然后所有线程,在其中共享您的CPU
。它不确定哪个线程将在之前完成以及哪个线程将最后完成。最后开始的Thread
可能最先结束。
这完全取决于所使用的CPU
调度算法。
所以,如果你运行两个线程而没有在其中任何一个上调用sleep
或wait
(taht没有任何interrupting
),那么CPU分配将在两个线程之间切换Threads
。因此,在Thread B
完成之前,Thread A
肯定有可能获得CPU。
但是,当Thread B
处于休眠状态的时间足以让A
完成作业时,CPU
将被分配给线程A,直到那时为止。所以它有足够的时间来完成它的工作(前提是你只有两个线程A
和B
当时处于runnable
状态。
这就是为什么multithreading
代码的结果永远不会相同的原因。它在多次运行中不断变化。因此,当您运行代码10 - 15
次时。你可以看到这种差异。
为确保您的Thread A
在Thread 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变量意味着: 这个变量的值永远不会被线程本地缓存:所有读写都将直接进入“主存”; 对变量的访问就好像它被包含在同步块中一样,自身同步。
试一试!