这在JLS的 Threads and
Locks
部分中进行了描述。
根据同步顺序定义何时需要线程从主存储器读取线程,并且该线程发生在顺序之前。基本上说,为了使 读取 产生最后 写入 的值,需要在读取 之前
进行写入。
事前发生的关系大致上是根据锁定/解锁动作定义的,并且(除了少数例外)归结为使用同步方法和块。除非您要处理volatile变量,否则最重要的是通常需要同步对共享数据的所有访问,最好通过
AtomicBoolean,a
BlockingQueue或其他一些java.util.concurrent类。
17.4.4同步顺序
每个执行都有一个同步顺序。同步顺序是执行中所有同步动作的总顺序。对于每个线程t,t中的同步动作(第17.4.2节)的同步顺序与t的程序顺序(第17.4.3节)一致。
同步动作在动作上引发了 同步关系 ,定义如下:
- 监视器m上的解锁操作与m上的所有后续锁定操作(其中同步根据同步顺序定义)同步。
- 对易失性变量(第8.3.1.4节)v的写操作与任何线程对v的所有后续读取进行同步(其中,后续操作根据同步顺序定义)。
- 启动线程的动作与它启动的线程中的第一个动作同步。
*将默认值(零,false或null)写入每个变量与每个线程中的第一个动作同步。尽管在分配包含变量的对象之前将默认值写入变量似乎有些奇怪,但是从概念上讲,每个对象都是使用默认初始化值在程序开始时创建的。- 线程T1中的最终操作与另一个线程T2中检测到T1已终止的任何操作同步。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). }}


