在这些情况下,方法to将在方法最后的unlock活动执行之前被强迫引发store(存储)这个被赋的值到主存储器中。当然,方法fro则必须use(使用)a和b(以这种顺序)并且必须从主存储器中load(装入)a和b。
所有的活动的顺序可以用下图表示:
这里一个从活动A到活动B的箭头表示A必须领先于B。
在主存储器中发生的活动是以何种顺序所运行的?注意规则不请求write(写入)a发生在write(写入)b之前;当然也不会在请求read(读取)a之前read(读取)b。当然,即使方法to被同步了,方法fro没有被同步,也是这样。所以没有东西可以在lock和unlock之间防止read活动的发生。(关键是定义一个同步的方法没有使它自己的行为成为不可分的)
作为结果,方法fro仍然能够获得a的1或3,并且可获得b的2或4。特别的,fro可能会观察a的值1和b的值4。因此,虽然to向a赋了值,并且对b也赋了值,而对主存储器的write活动可能被另一个线程所观察以至生产相反的顺序。
最后,假如to和fro都被同步了:
|
这种情况下,fro方法的活动不能与方法to相混杂,并且fro将显示“a=1,b=2”或“a=3,b=4”。
线程
线程通过内在的类Thread和ThreadGroup被建立并且管理。建立一个Thread对象建立一个线程,并且这是建立一个线程的唯一方法。当线程被建立,它尚未被激活;当它的方法被调用时,它才开始运行。
被个线程有一个priority(优先权)。当有处理资源的竞争时,有优先权的线程比有低优先权的线程有权占用资源。无论如何,保证有最高优先权的线程将被一直运行,并且线程优先权不能用于相互排斥的可靠性实现。
锁与同步
一把锁与每个对象相联系。Java程序设计语言并不提供一种将lock和unlock活动分离的方法;取而代之的是,它们被高层结构被隐蔽地运行,并安排与相应的活动成相配。
注意,无论如何,Java虚拟机提供了分离monitorenter和monitorexit的指令,以实现lock和unlock活动。
同步表达式计算一个对象的声明;它需要引发一个对于这个对象的lock活动,并且并且并不进一步处理,直到lock活动已经成功完成。(由于锁的规则避免主存储器被分享直至其它的线程准备引发一个或更多的unlock活动。而lock活动可能会被延期执行)。在lock活动被执行之后,synchronized(同步)语句被执行。如果语句的执行结束时,无论正常或异常,同一锁上的一个unlock活动会被自动地引发。
当一个同步的方法被调用时,自动引发一个lock活动;它的语句直到lock活动成功地结束才被执行。如果这个方法是一个实例方法,它锁定联系于被调用的实例的锁(这样,整个方法语句的运行中对象将被认作为this)。如果方法是static,那么它锁定联系于称为类的,方法就是定义的类对象的锁。如果方法的语句的执行结束,无论正常或异常,同一把锁的一个unlock活动会被自动引发。
最好的实践就是,如果一个变量不断地被一个线程赋值,并且被另一个使用和赋值,那么所有的对于变量的访问都被附加于同步的方法或同步的状态的变量。
Java程序设计语言不能防止,也不能请求侦察死锁条件。程序需要使用传统的技术防止死锁,线程保持(直接或间接地)锁定在多对象上,需要使用避免死锁的常用技术,建立高层次锁定,以防死锁。
Wait Sets(等待装置)和Notification(通告)
每个对象,都外加地联合于一把锁,联合于wait set(等待装置),这是一种线程的装置。当一个对象被首次建立,它的wait set(等待装置)是空的。
Wait sets被类的对象的方法wait,notify,和notifyAll所使用,这些方法也与线程中安排时序的机制相互作用。
方法wait只有当当前的线程(称之为T)已经锁定了一个对象锁时,才能被调用。假如线程T实际上引发了N锁定活动,它还没有与unlock活动相匹配。Wait方法就对当前的线程中的对象加入wait sets 装置,取消当前线程的关于线程时间安排,并且引发N unlock活动来解开锁。线程T则睡眠直到以下的三件事中的一件发生:
·一些其它的线程为这个对象调用notify方法,并且线程T被告知中断。
·一些其它的线程为这个对象调用notifyAll方法。
·如果线程T调用了wait方法,并指定了一个超时的间隔,且指定的时间已经到了。
线程T从wait set和重新有效的线程时间安排中删除。它再一次地锁定对萌(可能与其它线程会有竞争);一旦它获得对锁的控制。那么它就引发N-1的额外的lock活动并且从调用wait方法中返回。因此,从wait方法中返回,当wait方法被调用时,对象的锁的状态是严密而正确的。
Notify方法只有当当前的进程已经锁定了对象锁时才被调用。如果对象的wait set不空,那么一些中断的线程会从wait set和重新有效的线程时间安排表中删除。(当然,这个线程将不可能被处理,直到当前进程释放这把锁)
NotifyAll方法只有当当前的线程已经锁定了对象的锁时才被调用。每个在对象的wait set的线程从wait set中和重新有效的线程安排表时删除。(当然,那些线程直到当前线程释放这把锁时才能被处理)