volatile关键字

  volatile是一个类型修饰符(type specifier),就像大家更熟悉的const一样,它是被设计用来修饰被不同线程访问和修改的变量。volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。

特点:

  1. 其他线程对变量的修改可以立即反应到当前线程中
  2. 确保当前线程对volatile变量的修改,能即时的写到共享内存中,并被其他线程所见
  3. 使用volatile修饰的变量,编译器会保证其有序性

正确使用volatile变量的条件:

  1. 对变量的写操作不依赖于当前值
  2. 该变量没有包含在具有其他变量的表达式中

   第一个条件显示volatile变量不适合用作线程安全计数器,因为在多线程中,增量操作(count++)虽然看上去是一个单独的操作,但实际上是由读取-->修改-->写入操作组成的组合操作,volatile并不能保证整个过程都是原子操作。

适用场景:

  • 状态标志

     这种类型的状态标记的一个公共特性是:通常只有一种标记状态的来回转换,而且状态变量通常是原子变量

  • 一次性安全发布

   缺乏同步会导致无法实现可见性,这使得确定何时写入对象引用而不是原语值变得更加困难,此时我们可以利用volatile实现对象的安全发布。这种模式的一个必要条件是:被发布的对象必须是线程安全的,或者是有效的不可变对象(有效不可变意味着对象的状态在发布后不会被修改)。volatile类型的引用可以确保对象的发布形式的可见性,但是如果对象的状态在发布后发生改变,那么需要额外的同步

  • 独立观察

   安全使用volatile的另一种简单模式是:定期“发布”观察结果供程序内部使用。使用该模式的另一种应用程序是收集程序的统计信息,例如记录一个程序中最后登录的用户,可以将反复使用lastUser引用来发布值,以便程序的其他部分使用。这个模式要求被发布的值是有效不可变的--即值的状态在发布后不会改变。

  • “volatile bean”模式

   volatile bean 模式适用于将 JavaBeans 作为“荣誉结构”使用的框架。在 volatile bean 模式中,JavaBean 被用作一组具有 getter 和/或 setter 方法 的独立属性的容器。volatile bean 模式的基本原理是:很多框架为易变数据的持有者(例如 HttpSession)提供了容器,但是放入这些容器中的对象必须是线程安全的。

   在 volatile bean 模式中,JavaBean 的所有数据成员都是 volatile 类型的,并且 getter 和 setter 方法必须非常普通 —— 除了获取或设置相应的属性外,不能包含任何逻辑。此外,对于对象引用的数据成员,引用的对象必须是有效不可变的。(这将禁止具有数组值的属性,因为当数组引用被声明为 volatile 时,只有引用而不是数组本身具有 volatile 语义)。对于任何 volatile 变量,不变式或约束都不能包含 JavaBean 属性。

  • 开销较低的读-写锁策略

   当读操作远远超过写操作时,我们可以结合使用内部锁和volatile变量来减少公共代码路径的开销。如果更新不频繁的话,该方法可以实现更好的性能,因为读路径的开销仅仅涉及到volatile的读操作,这通常要由于一个无竞争的锁的开销。

        

public class ChessyCounter{
private volatile int count;
public int getValue(){return this.count;}
public synchronized int increment(){
return count++;
}
}

  

   之所以将这种技术称之为“开销较低的读-写锁”,是因为使用的不同的同步机制进行读写操作。由于在读操作中使用volatile可以确保当前值的可见性,所以可以使用锁进行所有变化的操作,使用volatile进行只读操作。

总结:

  与锁相比,Volatile 变量是一种非常简单但同时又非常脆弱的同步机制,它在某些情况下将提供优于锁的性能和伸缩性。如果严格遵循 volatile 的使用条件 —— 即变量真正独立于其他变量和自己以前的值 —— 在某些情况下可以使用 volatile 代替 synchronized 来简化代码。然而,使用 volatile 的代码往往比使用锁的代码更加容易出错。本文介绍的模式涵盖了可以使用 volatile 代替 synchronized 的最常见的一些用例。遵循这些模式(注意使用时不要超过各自的限制)可以帮助您安全地实现大多数用例,使用 volatile 变量获得更佳性能。

      

synchronized关键字

  synchronized 关键字,代表这个方法加锁,相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程B(或者C、 D等)正在用这个方法(或者该类的其他同步方法),有的话要等正在使用synchronized方法的线程B(或者C 、D)运行完这个方法后再运行此线程A,没有的话,锁定调用者,然后直接运行。它包括两种用法:synchronized 方法和 synchronized 块。

  Java语言的关键字,可用来给对象和方法或者代码块加锁,当它锁定一个方法或者一个代码块的时候,同一时刻最多只有一个线程执行这段代码。当两个并发线程访问同一个对象object中的这个加锁同步代码块时,一个时间内只能有一个线程得到执行。另一个线程必须等待当前线程执行完这个代码块以后才能执行该代码块。然而,当一个线程访问object的一个加锁代码块时,另一个线程仍可以访问该object中的非加锁代码块。

特点:

  1. 被synchronized修饰的方法或代码块同一时间只能被一个线程使用
  2. 当一个线程访问Object的一个synchronized代码块时,另一个线程仍可访问该对象的非synchronized代码块
  3. 当一个线程访问Object的一个synchronized代码块时,其他线程对object中所有其它synchronized(this)同步代码块的访问将被阻塞。
  4. 当一个线程访问Object的一个synchronized(this)同步代码块时,它就获得了这个Object的对象锁。结果,其他线程对该Object对象所有的同步代码部分的访问都暂时被阻塞

使用场景:

  • synchronized方法:

  通过在方法声明中加入synchronized关键字,synchronized 方法控制对类成员变量的访问:每个类实例对应一把锁,每个 synchronized 方法都必须获得调用该方法的类实例的锁方能执行,否则所属线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类实例,其所有声明为 synchronized 的成员函数中至多只有一个处于可执行状态(因为至多只有一个能够获得该类实例对应的锁),从而有效避免了类成员变量的访问冲突(只要所有可能访问类成员变量的方法均被声明为 synchronized)

  • synchronized代码块:

  通过 synchronized关键字来声明synchronized 块,其中的代码必须获得对象 syncObject (如前所述,可以是类实例或类)的锁方能执行,具体机制同前所述。由于可以针对任意代码块,且可任意指定上锁的对象,故灵活性较高。

  • synchronized方法和synchronized同步代码块的区别:
  1. synchronized同步代码块只是锁定了该代码块,代码块外面的代码还是可以被访问的。
  2. synchronized方法是粗粒度的并发控制,某一个时刻只能有一个线程执行该synchronized方法。
  3. synchronized同步代码块是细粒度的并发控制,只会将块中的代码同步,代码块之外的代码可以被其他线程同时访问。

final关键字

特点:

  1. final类不能被继承,没有子类,final类中的方法默认是final的。
  2. final方法不能被子类的方法覆盖,但可以被继承。
  3. final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
  4. final不能用于修饰构造方法。

使用场景:

  • 修饰变量

    当final修饰一个变量的时候一般把他作为常量,通常和static关键字配合使用。一般static修饰的常量都用大写字母来表示。

  • 修饰方法

    当一个方法被final修饰后,表示该方法不能被子类重写,final方法有一个好处是比非final方法要快,因为在编译时已经静态绑定了,不需要在运行时在动态绑定。

  • 修饰类

    当一个类被final修饰后,表示该类是完整的,不能被继承,例如Java中String、Integer类都是final类

优点:

  1. final关键字提高了性能。JVM和Java应用都会缓存final变量
  2. final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销
  3. 使用final关键字,JVM会对方法、变量及类进行优化
  4. 不可变类创建不可变类要使用final关键字。不可变类是指它的对象一旦被创建了就不能被更改了。String是不可变类的代表。不可变类有很多好处,譬如它们的对象是只读的,可以在多线程环境下安全的共享,不用额外的同步开销等等

参考资料:

    java理论与实践:正确使用volatile变量

java内存模型(jMM)(二)的更多相关文章

  1. Java内存模型JMM与可见性

    Java内存模型JMM与可见性 标签(空格分隔): java 1 何为JMM JMM:通俗地讲,就是描述Java中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这 ...

  2. 多线程并发之java内存模型JMM

    多线程概念的引入是人类又一次有效压寨计算机的体现,而且这也是非常有必要的,因为一般运算过程中涉及到数据的读取,例如从磁盘.其他系统.数据库等,CPU的运算速度与数据读取速度有一个严重的不平衡,期间如果 ...

  3. 全面理解Java内存模型(JMM)及volatile关键字(转载)

    关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoad ...

  4. 全面理解Java内存模型(JMM)及volatile关键字(转)

    原文地址:全面理解Java内存模型(JMM)及volatile关键字 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型( ...

  5. 对多线程java内存模型JMM

    多线程概念的引入体现了人类重新有效压力寨计算机.这是非常有必要的,由于所涉及的读数据的过程中的一般操作,如从磁盘.其他系统.数据库等,CPU计算速度和数据读取速度已经严重失衡.假设印刷过程中一个线程将 ...

  6. Java并发编程:Java内存模型JMM

    简介 Java内存模型英文叫做(Java Memory Model),简称为JMM.Java虚拟机规范试图定义一种Java内存模型来屏蔽掉各种硬件和系统的内存访问差异,实现平台无关性. CPU和缓存一 ...

  7. Java内存模型JMM 高并发原子性可见性有序性简介 多线程中篇(十)

    JVM运行时内存结构回顾 在JVM相关的介绍中,有说到JAVA运行时的内存结构,简单回顾下 整体结构如下图所示,大致分为五大块 而对于方法区中的数据,是属于所有线程共享的数据结构 而对于虚拟机栈中数据 ...

  8. java内存模型(二)深入理解java内存模型的系列好文

    深入理解java内存模型(一)--基础 深入理解java内存模型(二)--重排序 深入理解java内存模型(三)--顺序一致性 深入理解java内存模型(四)--volatile 深入理解java内存 ...

  9. 什么是Java内存模型(JMM)

    什么是java内存模型 缓存一致性问题 在现代计算机中,因为CPU的运算速度远大于内存的读写速度,因此为了不让CPU在计算的时候因为实时读取内存数据而影响运算速度,CPU会加入一层缓存,在运算之前缓存 ...

  10. 深入理解Java内存模型JMM与volatile关键字

    深入理解Java内存模型JMM与volatile关键字 多核并发缓存架构 Java内存模型 Java线程内存模型跟CPU缓存模型类似,是基于CPU缓存模型来建立的,Java线程内存模型是标准化的,屏蔽 ...

随机推荐

  1. php static 变量声明

    <?phpfunction test($key){ static $array = array();  /* 静态变量是只存在于函数作用域中的变量,注释:执行后这种变量不会丢失(下次调用这个函数 ...

  2. jquery设置select选中的文本

    <select id="prov">  <option value="1">北京市</option>  <option ...

  3. Python2.7.9 编码问题

    最近学一学网络爬虫,遇到第一件头疼的事情就是编码问题, 看了很多教程讲得不清楚, 现在整理一下,希望以后查看方便一些 使用   sys.getdefaultencoding()   查看Python的 ...

  4. eclipse netbeans 代码模板

    eclipse  代码模板  插入slf4j ${:import(org.slf4j.Logger,org.slf4j.LoggerFactory)} private static final Log ...

  5. 在windows上编译wireshark源代码

    终于在windows上成功编译了wireshark源代码,个中酸辛,都是泪..只能说要多试! windows上编译wireshark共用到三个东西:wireshark源代码.python.cygwin ...

  6. C primer 编程练习 (不断更新)

    目前在看<C Primer>,以后会经常在这篇博客里更新课后的编程练习题 第二章:编程练习 2.1 #include <stdio.h> int main(void) { pr ...

  7. 浅谈https\ssl\数字证书

    全球可信的SSL数字证书申请:http://www.shuzizhengshu.com 在互联网安全通信方式上,目前用的最多的就是https配合ssl和数字证书来保证传输和认证安全了.本文追本溯源围绕 ...

  8. 自己(转)String、StringBuffer与StringBuilder之间区别

    String.StringBuffer与StringBuilder之间区别   最近学习到StringBuffer,心中有好些疑问,搜索了一些关于String,StringBuffer,StringB ...

  9. SpringCloud教程 | 第六篇: 分布式配置中心(Spring Cloud Config)(Finchley版本)

    在上一篇文章讲述zuul的时候,已经提到过,使用配置服务来保存各个服务的配置文件.它就是Spring Cloud Config. 一.简介 在分布式系统中,由于服务数量巨多,为了方便服务配置文件统一管 ...

  10. 第一课 了解SQL

    1.1数据库基础 数据库:数据库是一个以某种有组织的方式存储的数据集合,可以想象是一个文件柜 数据库管理软件:DBMS用来操做创建数据库的软件 表:某种特定类型数据的结构化清单,数据库的下一层就是表 ...