volatile是轻量级的synchronized,在多处理器(多线程)开发中保证了共享变量的“可见性”。可见性表示当一个线程修改了一个共享变量时,另外一个线程能读到这个修改的值。正确的使用volatile,能比synchronized的使用和执行成本更低,因为它不会引起线程上下文的切换和调度。使用时只需要把字段声明成volatile即可。
 
处理器实现,有volatile变量修饰的共享变量进行写操作的时候会出现LOCK前缀指令。触发两件事情,1、将当前处理器缓存行的数据写回到系统内存。2、这个写会内存的操作会使在其他CPU里缓存了该内存地址的数据无效。
 
正常情况下,处理器不直接和内存进行通信,而是先将系统内存的数据读取到内部缓存(L1,L2或其他),但操作完不知道何时会写到内存。如果对声明了volatile的变量进行写操作,JVM就会向处理器发送一条LOCK前缀的指令,将这个变量所在的缓存行的数据写回到内存。但是,就算写回到内存,如果其他处理器缓存的值还是旧的,再执行计算操作就会有问题。所有,在多处理器下,为了保证各个处理器缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己的缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行设置为无效状态,当处理器对这个数据进行修改操作的时候,会重新从系统内存中把数据都到处理器缓存里。
 
volatile两个实现原则
1)在执行指令期间,声言处理器的LOCK#信号。在多处理器环境中,LOCK#信号确保在声言该信号期间,处理器可以独占任何共享内存。在老的处理器中,LOCK#信号一般锁总线,但是锁总线的开销比较大,现在的处理器,一般锁缓存。
2)一个处理器的缓存回写到内存内会导致其他处理器的缓存无效。
 
这里提到的总线锁和缓存锁,可以参考:https://blog.csdn.net/summermangozz/article/details/75098773
 
volatile所能保证的是可见性,不能保证Java程序的原子性。
还是以最常用的i++来说吧,包含3个步骤
1,从内存读取i当前的值
2,加1
3,把修改后的值刷新到内存
对于普通变量来说多线程下1,2之间被中断,其它线程修改了i的值,那原来已经在1,2之间被中断的线程的i的值就已经无效了,所以多线程是不安全的。
另外对于普通变量来说,步骤1并不是每次都会从内存中读取,步骤3也并不会每次都保证会立即刷新到内存。
所以这里有两个问题,可见性和原子性,viloate只能保证可见性,即步骤1每次都重新读,步骤3每次都立即刷新到主内存。但1,2之间仍然会被中断,多个线程交叉修改,所以仍然不安全。
 
synchronized,Java中每个对象都可以作为锁。3种形式:1、普通同步方法,锁就是当前实例对象。2、静态同步方法,锁是当前类的class对象。3、同步方法块,锁是Synchronized括号里配置的对象。
 
JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,但是实现细节不同,代码块同步是实用monitorenter和monitorexit指令实现的,而方法同步是实用另外一种方式实现。monitorenter插入在代码块的开始位置,而monitorexit是插入在代码块结束和异常处,保证每个monitorenter必须有对应的monitorexit配对,任何一个对象都有monitor关联,当且一个monitor被持有后,就处于锁定状态。线程执行到monitorenter指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获取对象的锁。
 
Java SE1.6为了减少获取锁和释放锁的性能消耗,引入了“偏向锁”和“轻量级锁”,总共四种状态:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态。
 
上图,主要是对象头的详细说明。
 
偏向锁
主要针对只有一个线程访问同步块的场景(很多情况下不存在多线程竞争,总是由同一个锁多次获得),如果是这种情况直接在对象头和栈帧中的锁记录里存储锁偏向的线程ID,以后该线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁。Word里是否存储着指向当前线程的偏向锁。如果测试成功,表示线程已经获得了锁。如果测试失败,则需要再测试一下Mark Word中偏向锁的标识是否设置成1(表示当前是偏向锁):如果没有设置,则使用CAS竞争锁;如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。
偏向锁的撤销,等到竞争出现才释放锁的机制,所以当其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁。
缺点是如果线程间存在锁竞争,会带来额外的锁撤销的消耗。
 
轻量级锁
竞争的线程不会阻塞,提高程序的响应速度,但是如果始终得不到锁竞争的线程,使用自旋会消耗CPU,适用于追求响应时间,同步块执行速度非常快。
 
重量级锁
线程竞争不适用自旋,不会消耗CPU,但是会线程阻塞,响应时间缓慢。适用于追求吞吐量,同步块执行速度较长。
 
上图是synchronized原理图,分享一下。 
 
转载请注明出处。
作者:wuxiwei
出处:http://www.cnblogs.com/wxw16/p/8926535.html 

Java多线程volatile和synchronized总结的更多相关文章

  1. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

  2. Java多线程6:Synchronized锁代码块(this和任意对象)

    一.Synchronized(this)锁代码块 用关键字synchronized修饰方法在有些情况下是有弊端的,若是执行该方法所需的时间比较长,线程1执行该方法的时候,线程2就必须等待.这种情况下就 ...

  3. Java多线程5:Synchronized锁机制

    一.前言 在多线程中,有时会出现多个线程对同一个对象的变量进行并发访问的情形,如果不做正确的同步处理,那么产生的后果就是“脏读”,也就是获取到的数据其实是被修改过的. 二.引入Synchronized ...

  4. java多线程-----volatile

    谈谈Java中的volatile   内存可见性 留意复合类操作 解决num++操作的原子性问题 禁止指令重排序 总结 内存可见性 volatile是Java提供的一种轻量级的同步机制,在并发编程中, ...

  5. Java多线程简析——Synchronized(同步锁)、Lock以及线程池

    Java多线程 Java中,可运行的程序都是有一个或多个进程组成.进程则是由多个线程组成的.最简单的一个进程,会包括mian线程以及GC线程. 线程的状态 线程状态由以下一张网上图片来说明: 在图中, ...

  6. 看一遍就懂,详解java多线程——volatile

    多线程一直以来都是面试必考点,而volatile.synchronized也是必问点,这里我试图用容易理解的方式来解释一下volatile. 来看一下它的最大特点和作用: 一 使变量在多个线程间可见 ...

  7. Java 多线程 - Volatile关键字

    作者: dreamcatcher-cx 出处: <http://www.cnblogs.com/chengxiao/> 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明 ...

  8. Java多线程之二(Synchronized)

    常用API method 注释 run() run()方法是我们创建线程时必须要实现的方法,但是实际上该方法只是一个普通方法,直接调用并没有开启线程的作用. start() start()方法作用为使 ...

  9. java多线程3:synchronized

    线程安全 多个线程共同访问一个对象的实例变量,那么就可能出现线程不安全的问题. 先看一段代码示例,定义一个对象 MyDomain1 public class MyDomain1 { private i ...

随机推荐

  1. Docker学习笔记 - Docker的基本概念

    一.cs架构 Docker客户端:本地或远程 Docker服务端:守护进程Docker Daemon 二.基本概念 Docker镜像:打包阶段,层叠的只读文件系统,引导->root(ubuntu ...

  2. 阿里云API网关(10)服务网关业务流程

    一.开放api 二.调用api 三.开发指南 四.其他 1.调试 2.测试 3.mock 4.发布 5.checklist

  3. React-Native(六):React Native完整的demo项目

    该项目在http://www.lcode.org/study-react-native-opensource-two/上发现 更有意思的发现这个网站https://juejin.im/是采用vue.j ...

  4. Linux下C语言socket通信实现发送读取的文件内容--简单实现代码

    本次代码涉及到的内容:socket通讯,文件读取 读取的文件以及文件位置: 要读取的文件和c文件在同一个目录下.客户端(client)读取的是123.xml,服务端(server)读取的是23.xml ...

  5. jacascript 滚动 scroll 与回到顶部

    前言:这是笔者学习之后自己的理解与整理.如果有错误或者疑问的地方,请大家指正,我会持续更新! 滚动 scroll scrollHeight 表示元素的总高度,包括由于溢出而无法展示在网页的不可见部分: ...

  6. SQL语句 (一)

    1 SQL语句分类: 数据查询语句(DQL): SELECT 数据操纵语言 (DML): INSERT.UPDATE.DELETE 数据定义语言 (DDL): 数据控制语言 (DCL): GRANT. ...

  7. C++ 多态的实现及原理

    C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数.如果对象类型是派生类,就调用派生类的函数:如果对象类型是基类 ...

  8. jmeter出现卡死或内存溢出的解决方案

    故事背景:在初次使用jmeter的时候,把线程设置较大值的时候,jmeter工具很容易就卡死了,导致每次做压测的时候都无法顺利完成,非常的闹心,通过各种方法寻找解决方案,终于找到了一个比较靠谱的方法, ...

  9. Linux下的MySQL5.7.14启动方法

    启动MySQL服务: systemctl start mysql 启动MySQL服务(安全方式): mysqld_safe --user=mysql & 登录MySQL(有密码): mysql ...

  10. angularjs中的几种工具方法

    1.比较两个字符串是否相等 2.对象形式转化成json和json转化成字符串形式 3.便利对象遍历数组 4.绑定数据 5.多个app功能模块的实现 <!doctype html><h ...