volatile关键词:用来对共享变量的访问进行同步,上一次写入操作的结果对下一次读取操作是肯定可见的。 (在写入volatile变量值之后,CPU缓存中的内容会被写回内存;在读取volatile变量时,CPU缓存中的对应内容会被置为失效,重新从主存中进行读取),volatile 不使用锁,性能优于synchronized关键词。
用来确保对一个变量的修改被正确地传播到其他线程中。
volatile 修复符的另一个作用是提供内存屏障(memory barrier),例如在分布式框架中的应用。简单的说,就是当你写一个 volatile 变量之前,Java 内存模型会插入一个写屏障(write barrier),读一个 volatile 变量之前,会插入一个读屏障(read barrier)。意思就是说,在你写一个 volatile 域时,能保证任何线程都能看到你写的值,同时,在写之前,也能保证任何数值的更新对所有线程是可见的,因为内存屏障会将其他所有写的值更新到缓存。
volatile本身不保证获取和设置操作的原子性,仅仅保持修改的可见性。
volatile 还提供顺序,JVM 或者 JIT为了获得更好的性能会对语句重排序,但是 volatile 类型变量即使在没有同步块的情况下赋值也不会与其他语句重排序。
原子性:
long 和double 类型的赋值不是一条指令,指令是可切割的。
int等不大于32位的数据都是原子性读写,而long 和double 是分步进行2次32位的写操作。
但是java内存模型保证声明为volatile的long和double变量的get和set操作是原子的。jvm spec引用中的最后一段说到,jvm可以很轻易的将变量操作变成原子性的,但是却受到了当前硬件的约束,因为流行的微处理器还是32bit居多,因此64bit的变量需要拆分成两次,但如果是64bit处理器就能满足64bit变量的原子性操作了。 看来,随着硬件的不断改进,这个问题以后将不会是问题了吧
synchronized(this)与synchronized(static XXX)
前者是对类的对象实现线程互斥,static是属于类的公有,所以是对该类实现线程互斥。
伪共享:
不同处理器修改了位于同一cache line的数据,这导致cache line失效,强制内存更新来维护cache一致性。
避免伪共享的主要方法是代码检查(code inspection)。当线程访问全局变量或者动态分配的共享数据结构是伪共享的潜在来源。注意到伪共享可能不太容易识别出来(obscured),因为线程访问的是完全不同的而实际上碰巧在主存中相邻的全局变量。线程局部存储或者局部变量不会是伪共享的来源。
局部变量:
但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。
不变对象包含可变对象?
是的,我们是可以创建一个包
含可变对象的不可变对象的,你只需要谨慎一点,不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝。
用来确保对一个变量的修改被正确地传播到其他线程中。
volatile 修复符的另一个作用是提供内存屏障(memory barrier),例如在分布式框架中的应用。简单的说,就是当你写一个 volatile 变量之前,Java 内存模型会插入一个写屏障(write barrier),读一个 volatile 变量之前,会插入一个读屏障(read barrier)。意思就是说,在你写一个 volatile 域时,能保证任何线程都能看到你写的值,同时,在写之前,也能保证任何数值的更新对所有线程是可见的,因为内存屏障会将其他所有写的值更新到缓存。
volatile本身不保证获取和设置操作的原子性,仅仅保持修改的可见性。
volatile 还提供顺序,JVM 或者 JIT为了获得更好的性能会对语句重排序,但是 volatile 类型变量即使在没有同步块的情况下赋值也不会与其他语句重排序。
原子性:
long 和double 类型的赋值不是一条指令,指令是可切割的。
int等不大于32位的数据都是原子性读写,而long 和double 是分步进行2次32位的写操作。
但是java内存模型保证声明为volatile的long和double变量的get和set操作是原子的。jvm spec引用中的最后一段说到,jvm可以很轻易的将变量操作变成原子性的,但是却受到了当前硬件的约束,因为流行的微处理器还是32bit居多,因此64bit的变量需要拆分成两次,但如果是64bit处理器就能满足64bit变量的原子性操作了。 看来,随着硬件的不断改进,这个问题以后将不会是问题了吧
synchronized(this)与synchronized(static XXX)
前者是对类的对象实现线程互斥,static是属于类的公有,所以是对该类实现线程互斥。
伪共享:
不同处理器修改了位于同一cache line的数据,这导致cache line失效,强制内存更新来维护cache一致性。
避免伪共享的主要方法是代码检查(code inspection)。当线程访问全局变量或者动态分配的共享数据结构是伪共享的潜在来源。注意到伪共享可能不太容易识别出来(obscured),因为线程访问的是完全不同的而实际上碰巧在主存中相邻的全局变量。线程局部存储或者局部变量不会是伪共享的来源。
局部变量:
但是在管理环境下(如 web 服务器)使用线程局部变量的时候要特别小心,在这种情况下,工作线程的生命周期比任何应用变量的生命周期都要长。任何线程局部变量一旦在工作完成后没有释放,Java 应用就存在内存泄露的风险。
不变对象包含可变对象?
是的,我们是可以创建一个包
含可变对象的不可变对象的,你只需要谨慎一点,不要共享可变对象的引用就可以了,如果需要变化时,就返回原对象的一个拷贝。