Java Concurrency in Practice notes 2

Chapter3, 共享对象

    编写正确的并发程序的关键在于对共享的,可变的状态进行访问管理。可以使用同步来避免多个线程在同一时间访问同一数据,也可以使用构建技术来安全的共享和发布对象,使多个线程可以安全的访问它们。下面讲述的将是如何安全的共享和发布对象。

    首先要提及的是内存可见性。Synchronized关键字不仅可以用于原子操作或者划定临界区,也可以用于保证变量的内存可见性。当访问一个共享的可变变量时,为了过期数据保证一个线程对数值进行的写入对其他线程都可见,在读取和写入线程时必须使用公共的锁进行同步。在没有完全同步的情况下,编译器,处理器运行时安排操作的执行顺序可能完全出人意料。在没有进行适当的同步的多线程程序中尝试推断那些发生在内存中的动作时,是很不靠谱的。

    没有恰当同步导致的第一个问题是会产生过期数据,过期数据可能发生在一个变量山,也可能发生在多个变量上,这就让程序的运行充满了不确定性。第二个问题是非原子性的64位操作。一般情况下,过期数据至少是真实的数值,除了一个例外的:没有声明为volatile的64位数值变量(double和long)。Java存储模型要求获取和存储操作都是原子性的,但对于非volatile的long和double变量,JVM允许将64位的读或写画分为两个32位的操作。如果读和写发生在不同的线程,这种情况读取一个非volatile类型的long就有可能一个值的高32位和另一个值的低32位。因此在多线程中使用共享的,可变的long或double类型也可能是不安全的,除非将它们声明位volatile类型或者用锁保护起来。

    Java提供了另外一种弱同步形式:volatile变量。当一个域声明为volatile类型后,编译器和运行时会监视这个变量:它是共享的,而且对它的操作不会与其他的内存操作一起被重排序。volatile变量不会缓存在寄存器或者对其他处理器隐藏的地方,所以读取volatile类型的变量总会返回由某一线程所写入的最新值。但在实际运用时并不推荐过度依赖volatile变量提供的可见性,依赖volatile变量来控制状态可见性的代码比使用锁的代码更脆弱和难以理解。只有当volatile变量能够简化实现和同步策略的验证时才使用它们,当验证正确性必须推断可见性问题时(veryfing correctness would require subtle reasoning about visibility),应该避免使用volatile变量。正确使用volatile变量的方式包括:用于确保它们所引用的对象状态的可见性,或者用于标识重要的声明周期事件(如初始化或关闭)的发生。如果满足下列条件才能使用volatile变量:

                     写入变量时并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值;

                     变量不需要其他的状态变量共同参与不变约束;

                     访问变量时没有其他的原因需要加锁。

 

>>>>>to be continue>>>>>>

你可能感兴趣的:(java,jvm,多线程)