Java 跨线程的对象可见性
对于跨线程发布数据和数据更改,我有一个普遍的疑问。 例如,考虑下面的类。Java 跨线程的对象可见性,java,concurrency,Java,Concurrency,对于跨线程发布数据和数据更改,我有一个普遍的疑问。 例如,考虑下面的类。 public class DataRace { static int a = 0; public static void main() { new MyThread().start(); a = 1; } public static class MyThread extends Thread { public void run() { // Access a, b.
public class DataRace {
static int a = 0;
public static void main() {
new MyThread().start();
a = 1;
}
public static class MyThread extends Thread {
public void run() {
// Access a, b.
}
}
}
让我们关注main()
明明
new MyThread().start();
a = 1;
在这里,我们在MyThread启动后更改共享变量a,因此可能不是线程安全的发布
a = 1;
new MyThread().start();
然而,这一次,a中的更改被安全地跨新线程发布,因为Java语言规范(JLS)保证线程a在启动线程B时可以看到的所有变量对线程B都是可见的,这实际上就像在thread.start()中有一个隐式同步一样
在这种情况下,当两个线程都已生成后分配新变量时,是否可以保证新变量将安全发布到所有线程。i、 e.如果var b被另一个线程访问,是否保证将其值视为1。请注意,我不是在讨论在此之后对b的任何后续修改(这当然需要同步),而是jvm完成的第一次分配
谢谢
a = 1;
new MyThread().start();
对“a”的赋值将在对新线程调用start之前发生,因此新线程将始终打印值“1”
new MyThread().start();
a = 1;
在这种情况下,MyThread可以打印1或0作为“a”的值。我不完全确定这一点:
a = 1;
new MyThread().start();
。。。在这一点上,我不确定在新线程启动之前,a
的值是否会“刷新”到共享内存中。然而,规范明确提到了这一点。其中指出:
启动线程的操作与它启动的线程中的第一个操作同步
(之后的讨论基本上表明一切都会好起来的。)
我不确定您的上一个示例(带有b
)是关于什么的。MyThread().start()启动一个单独运行的新线程。int b=1的声明与您启动的线程无关。它在主线程中继续
如果两个线程同时读取或修改同一对象,或者如果两个线程以相反的顺序获得资源锁,从而每个线程都在等待另一个线程(这称为死锁),那么线程安全就是一个问题。我不确定您在问什么。我想你说的是对“a”变量的线程安全访问 问题不在于调用的顺序,而在于 -对的访问不是线程安全的。因此,在一个多线程更新和读取a的示例中,您永远无法保证您正在读取的“a”与您从中更新的值相同(其他线程可能已更改该值) -在多线程环境中,jvm不能保证更新的值保持同步。例如
Thread 1: a=1
Thread 2: a=2
Thread 1: print a <- may return 1
线程1:a=1
螺纹2:a=2
线程1:打印a我对这个问题有疑问:)
关于在并发执行线程之间访问共享资源的问题是一个相当容易理解和研究的主题
通常,如果您有一个由多个线程通过读/写语义访问的源,那么您将需要规范对该资源的访问。(这里的资源是静态int变量DataRace.a)
(我还+1 Steve B.在这里指出,这里的问题不是调用的顺序。)如果B是在DataRace.main()中创建的局部变量,正如代码片段所示,MyThread一开始就无法访问它,因此这个问题没有意义
如果b是主线程和MyThread线程之间的共享变量,则在没有适当内存屏障的情况下,它没有正确的可见性,因此MyThread可能无法及时看到正确的值。Java语言规范(JLS)保证当线程a启动线程B时,线程a可见的所有变量对线程B可见。因此,调用顺序在这里很重要?实际上,并发是由Brian Goetz等人编写的,Block使用Pugh或Gafter@nonsequitor更准确地说,Joshua Bloch是JCIP封面上列出的作者之一。当两个线程都已生成后分配新变量时,是否可以保证新变量将安全发布到所有线程。i、 e.如果var b被另一个线程访问,是否保证将其值视为1?这取决于具体情况。必须有特定的东西来保证读取线程不使用缓存的值,并且写入线程已将写入发布到共享内存。请参阅上述规范第17章,了解更多(脑力崩溃)细节。“刷新到共享内存”?(您是指堆吗?)不涉及刷新,因为这与流语义无关。在JVM中,值分配是原子的(对于long有一些小的警告)。执行a=1后,变量DataRace.a的值为1。如果线程是在语句a=1之前生成的,则不能保证它将看到DataRace.a的值。当然,也有可能线程启动并交换,主线程执行并设置a=1,然后另一个线程将看到a==1。更可能的情况是,它将看到a==0.>>“读取线程不使用缓存的值”这里没有读线程缓存b的值的意义,因为它以前从未存在过。乔恩,考虑最后的字符串MSG =“BLAH BLAH”;myExecutorService.submit(新日志任务(msg));其中loggingTask simple将消息写入某处,执行器服务不在同一个线程执行器中。你的意思是说,在这种情况下,我必须显式同步字符串“msg”?@baskin:假设CPU有一个缓存的内存值,其中包括包含b的区域。即使它以前没有显式读取,它是否肯定知道缓存无效?我不确定——也许,也许不是。我怀疑提交一份新的
Thread 1: a=1
Thread 2: a=2
Thread 1: print a <- may return 1