在一次面试中,被问到volatile与synschonized的区别,概念模模糊糊,今天做一个总结,加强自己的认识。

本文参考http://www.cnblogs.com/dolphin0520/p/3920373.html,主要对自己的认识做个总结。

valitile这个关键词,不局限于java中,其实很多语言中都有这个关键词。由于自己之前对于多线程的编程接触比较少,而且对于java的内存模型不是很了解,所以今天做一个总结。

内存模型

现在想想大学那会学的操作系统真是太有用了,可惜当时没有认真学,很多编程的问题,都可以归结到操作系统,而且很多优秀的设计都是从操作系统来的。不说了,一把心酸泪。还是努力弥补吧。

计算机的主要运算是由cpu,内存之间交互的,而他们之间的交互靠的是总线。但是由于cpu的速度远远大于内存的,所以在cpu旁边往往会设计一级二级缓冲,作用就是中和cpu与内存之间的速度。

但是让我们想想,如果程序是单线程的,基本没啥问题,因为数据不会存放着多个缓冲中,也就不涉及一致性的问题,但是当我们的程序里面有多线程的时候,可能不同的cpu会执行不同的线程,这样可能不同cpu的缓冲会持有同一个变量的不同副本,这样就有问题了。不同线程操作同一个变量,如何做到同步。

为了解决这个问题,那篇文章里面提到有两种方式。

  1.在访问的时候,总线加锁,也就是相当于人为将多线程,变为单线程,这样不会出现数据不一致的问题,但是这样带来了很大问题,就是效率大打折扣,体现不出多线程的优势。

  2.缓存一致性协议:

    这里我就引用别人的一段话:“缓存一致性协议,最出名的就是Intel 的MESI协议,MESI协议保证了每个缓存中使用的共享变量的副本是一致的。它核心的思想是:当CPU写数据时,如果发现操作的变

    量是共享变量,即在其他CPU中也存在该变量的副本,会发出信号通知其他CPU将该变量的缓存行置为无效状态,因此当其他CPU需要读取这个变量时,发现自己缓存中缓存该变量的

    缓存行是无效的,那么它就会从内存重新读取。”

  最后附上一张图,来总结下上面说的。

  

   多线程编程的概念

     1.原子性:

        这个概念在数据库里面也有,意思就是保证一个操作,要么完成,要不不做,而不能是做了一半。想必大家对这个最熟悉的应该就是银行的例子了吧。转账的时候,从我账户

        扣款和给对方账户加款,应该是原子操作,不能说从我账户扣了,但是对方账户没有增加。这是在多线程里面必须要避免的问题。

     2.可见性:

        这个词的意思就是当一个线程在修改了某一个变量之后,可以马上将改变刷新到别的线程,也就是说如果别的线程需要访问的时候,是访问的修改过后的值。

     3.有序性:

        程序执行的顺序,不一定会按照代码书写的顺序进行执行。而是编译器会对代码进行指令的优化,这样做的目的是为了保证程序执行的效率。这样做基本上对于单线程没啥问

        题,编译器保证做过优化后的代码和没做优化的代码,执行的结果是一样的。但是如果是多线程呢,这点编译器就无法保证。

     综上所述,一个多线程如果要正确的执行,就必须满足上面三个条件。如果不满足,则执行的结果就有可能出错。

    那么java里面通过什么样的方式来确保符合三种原则呢?

    java保证多线程(java内存模型)

      1.原子性:

        在java中,对基本数据类型的读取与赋值是原子操作的,要不操作成功,要么失败。

        那么怎么样的操作算原子操作呢,下面举两个例子:

          

int x = ;
int y = x;
y++;

第一条语句,是直接将值复制给x变量。第二条语句,首先读取x的值,然后再赋值给y。第三条语句,首先读取y的值,然后加一操作后,再赋值给y。

       所以上面三条语句只有第一条语句是原子操作。这也就解释了下面这段代码结果为啥不是人们的预期。

    

	public volatile static  int count = 0;
public static void main(String[] args) {
for(int i=0;i<10000;i++)
{
new Thread(new Runnable() { @Override
public void run() {
count++;
}
}).start();
}
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(count);
}

  这段代码执行的结果有时候会小于10000,原因就是count++不是原子操作,虽然用volatile修饰,但是也不起作用。

  那么上面这段代码如何正确运行呢,答案很显然,把count++变成原子操作即可,那么修改count的类型,如下

  public  static AtomicInteger count =new AtomicInteger(0);

  将count++变为count.getAndIncrement();

  这样保证了原子操作。结果也就是10000了。

      2.可见性:

        对于可见性,java提供了volatile关键词来修饰,上面已经用到过了。这个关键词保证他修改后的值,会马上更新到java的主存中,当其他线程再要读取的时候,就是读取的

        新的值,但是用这个关键词的时候,也得多多注意,就跟上面说的那种情况,也是不行的。当然上面的情况可以用加锁,或者 synchronized方式进行同步。保证结果。

      3.有序性:

        Java里面也是通过volatile来保证一定程度上的有序性。也可以通过 synchonized来保证多线程下的有序性。

        

  在《深入理解Java虚拟机》有这么一段话“

  观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”

  lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:

  1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;

  2)它会强制将对缓存的修改操作立即写入主存;

  3)如果是写操作,它会导致其他CPU中对应的缓存行无效。

  

  所以以后遇到问题的时候,还是得多从原理里面找答案。

  虽然volatile的性能比synchronized性能高,但是volatile的使用场景有所限制。因为它无法保证多线程下的原子性。

    

        

volatile synschonized的区别的更多相关文章

  1. volatile 与 synchronized 区别

    在Java中,为了保证多线程读写数据时保证数据的一致性,可以采用两种方式: 同步 如用synchronized关键字,或者使用锁对象. volatile 使用volatile关键字用一句话概括vola ...

  2. Java 中 static 和 volatile 关键字的区别?

    static指的是类的静态成员,实例间共享 volatile跟Java的内存模型有关,线程执行时会将变量从主内存加载到线程工作内存,建立一个副本,在某个时刻写回.valatile指的每次都读取主内存的 ...

  3. AtomicInteger 、Synchronized 和 volatile 之间的区别?

    AtomicInteger:无锁的线程安全整数??? Synchronized:同步 volatile:挥发性??? 参考文档:

  4. Java Volatile关键字 以及long,double在多线程中的应用

    概念: volatile关键字,官方解释:volatile可以保证可见性.顺序性.一致性. 可见性:volatile修饰的对象在加载时会告知JVM,对象在CPU的缓存上对多个线程是同时可见的. 顺序性 ...

  5. 有关volatile关键字和transient关键字

    (1)volatile关键字['vɑlətl]的作用 让变量每次在使用的时候,都从主存中取,而不是从各个线程的“工作内存”. 也就是说,volatile变量对于每次使用,线程都能得到当前volatil ...

  6. C/C++基础----特殊工具和技术 (重载new和delete,RTT,限定作用域的枚举类型,类成员指针,嵌套类,局部类,volatile,链接指示 extern “C”)

    重载new和delete 1调用operator new( 或new[])标准库函数分配足够大的.原始的.未命名的内存空间以便存储特定类型的对象 2编译器运行相应地构造函数以构造这些对象,并为其传入初 ...

  7. Java-JUC(二):Java内存模型可见性、原子性、有序性及volatile具有特性

    1.Java HotSpot JVM运行时数据区 Java内存模型即Java Memory Model,简称JMM.JMM定义了Java 虚拟机(JVM)在计算机内存(RAM)中的工作方式.JVM是整 ...

  8. jvm高级特性(5)(1)(原子性,可见性,有序性,volatile,概述)

    JVM高级特性与实践(十二):高效并发时的内外存交互.三大特征(原子.可见.有序性) 与 volatile型变量特殊规则 简介: 阿姆达尔定律(Amdahl):该定律通过系统中并行化与串行化的比重来描 ...

  9. Java并发:volatile内存可见性和指令重排

    volatile两大作用 1.保证内存可见性 2.防止指令重排 此外需注意volatile并不保证操作的原子性. (一)内存可见性 1 概念 JVM内存模型:主内存和线程独立的工作内存 Java内存模 ...

随机推荐

  1. 七牛云实现js上传

    七牛云的官方API写的一塌糊涂.最主要的,还是版本兼容的问题. 一.引入文件 引入了两个文件: 1.jquery-1.10.2.min.js 2.plupload.full.min.js 3.qini ...

  2. 安装linux操作系统--浪潮服务器

    一直都是在虚拟机上进行安装linux操作系统,在服务器上安装的很少,也没有碰到过没找到驱动的情况,例如什么raid卡驱动,网卡驱动等异常情况的发生. 这次安装了两台服务器,浪潮的提供的服务器,硬盘是两 ...

  3. nagios-解决监控页面上的乱码

    1. 前景 在nagios的监控页面上发现返回来的信息为乱码,如下图所示: 查看相关日志,发现正常显示汉字,如下: 2. 解决方法(以下两个步骤缺一不可) 主要原因分析如下: 在nagios服务器上发 ...

  4. DOM笔记(七):开发JQuery插件

    在上一篇笔记本中,讲解了如何利用jQuery扩展全局函数和对象:DOM笔记(六):怎么进行JQuery扩展? 在这篇笔记本中,将开发一个简单的动画插件,名称是example-plugin,用其实现一个 ...

  5. WebSocket with Flask

    转自:https://blog.shonenada.com/post/websocket-with-flask/ WebSocket with Flask HTML5 以前,HTML 还不支持 Web ...

  6. c++类使用

    一.C++定义类(注意:结束部分的分号不能省略) class 类名 { public: //公共的行为或属性 private: //公共的行为或属性 }; 注意:类的成员变量在定义时不能进行初始化, ...

  7. Dynamic Method Resolution

    [Dynamic Method Resolution] @dynamic directive 用于声明属性的方法dynamic loading,which tells the compiler tha ...

  8. C++11用于计算函数对象返回类型的统一方法

    [C++11用于计算函数对象返回类型的统一方法] 模板 std::result_of 被TR1 引进且被 C++11 所采纳,可允许我们决定和使用一个仿函数其回返值的类别.底下,CalculusVer ...

  9. HDU 2544 最短路 http://acm.hdu.edu.cn/showproblem.php?pid=2544

    //代码: //方法1:Dijkstra's Algorithm #include<stdio.h> #include<math.h> #include<string.h ...

  10. ThinkPHP C+F方式

    ThinkPHP常用C+F方法进行配置设置于缓存设置 比如常见的 C(F('smtp'),'smtp');表示获取F方法中smtp缓存,设置配置为smtp函数 C方法是ThinkPHP用于设置.获取, ...