java线程缓存刷新什么时候发生?

线程1:正在执行此循环

while(running) { // Do Task() } println("Done"); 

线程2设置为false如果运行是一个volatile变量,则thread1退出循环并打印“Done”。

我的问题是,如果运行不是易失性,Thread1何时从主内存中读取运行变量?

注意:我知道在关于同步和volatile变量的关系之前发生了,但是即使运行不是volatile或synchronized,线程1也会停止。 所以我的问题是线程1何时决定从主存储器中读取,因为没有同步或没有挥发性

这在JLS的Threads and Locks部分中描述。

当需要从主存储器读取线程时,根据同步顺序定义并在订单之前发生。 基本上它表示为了使读取产生上次写入的值,写入需要在读取之前发生

发生在之前的关系粗略地讲是根据锁定/解锁操作定义的(并且有一些例外)归结为同步方法和块的使用。 除非您正在处理volatile变量,否则底线通常是您需要同步对共享数据的所有访问,最好是通过AtomicBooleanBlockingQueue或其他一些java.util.concurrent类。

17.4.4同步顺序

每次执行都有一个同步顺序。 同步顺序是执行的所有同步动作的总顺序。 对于每个线程t,t中同步动作(第17.4.2节)的同步顺序与t的程序顺序(第17.4.3节)一致。

同步动作引发与动作的同步关系 ,定义如下:

  • 监视器m上的解锁操作与m上的所有后续锁定操作同步(其中后续操作根据同步顺序定义)。
  • 对volatile变量(第8.3.1.4节)的写入v与任何线程对v的所有后续读取同步(其中后续根据同步顺序定义)。
  • 启动线程的操作与其启动的线程中的第一个操作同步。
  • 向每个变量写入默认值(零,false或null)与每个线程中的第一个操作同步。 虽然在分配包含变量的对象之前将默认值写入变量似乎有点奇怪,但从概念上讲,每个对象都是在程序开始时使用其默认初始化值创建的。
  • 线程T1中的最终操作与另一个检测到T1已终止的线程T2中的任何操作同步。 T2可以通过调用T1.isAlive()或T1.join()来完成此操作。
  • 如果线程T1中断线程T2,则T1的中断与任何其他线程(包括T2)确定T2已被中断的任何点同步(通过抛出InterruptedException或通过调用Thread.interrupted或Thread.isInterrupted)。

同步边缘的源称为释放,目标称为获取。

17.4.5在订单之前发生

可以通过先发生关系来排序两个动作。 如果一个动作发生在另一个动作之前, 那么第一个动作在第二个动作之前是可见的并且在第

如果我们有两个动作x和y,我们写hb(x,y)来表示x发生在y之前。

  • 如果x和y是同一个线程的动作,并且x在程序顺序中出现在y之前,那么hb(x,y)。
  • 从对象的构造函数的末尾到该对象的终结器(第12.6节)的开始有一个发生前的边缘。
  • 如果动作x 后续动作y 同步 ,那么我们也有hb(x,y)。
  • 如果是hb(x,y)和hb(y,z),那么hb(x,z)。

应该注意的是,两个动作之间存在的先发生关系并不一定意味着它们必须在实现中以该顺序发生。 如果重新排序产生的结果与合法执行一致,则不是非法的。


更新:如果没有发生 – 在关系存在之前, 线程永远不需要“刷新其缓存” 。 这个问题和它接受的答案提供了一个具体的例子。

以下是已接受答案的略微修改版本:

 public class Test { static boolean keepRunning = true; public static void main(String[] args) throws InterruptedException { (new Thread() { public void run() { while (keepRunning) { } } }).start(); System.out.println(keepRunning); Thread.sleep(1000); keepRunning = false; System.out.println(keepRunning); // main thread ends here, but the while-thread keeps running. // (but not if you change the keepRunning to volatile). } } 

为什么不亲自尝试一下?

 public class ThreadTest { private static boolean running = true; public static void main(String[] args) throws InterruptedException { Thread t = new Thread() { public void run() { while( running ) { System.out.println( "Running."); } long l = System.nanoTime(); System.out.println( "Stopped at "+l); } }; t.start(); Thread.sleep( 1 ); running = false; long l = System.nanoTime(); System.out.println( "Stopping at "+l); } } 

它绝不是一个完美的测试,但它给你一个粗略的估计。 在我的机器上,差异大约是25毫秒。 当然它也必须执行System.out.println ,但这肯定不会花那么长时间。

由于thread1已经读取了运行,它将具有缓存值,并且这将保持有效一段时间。 这可能是我们或是ms,但可能更长。 这可能取决于架构,也取决于盒子的繁忙程度。