本文转载自http://shift-alt-ctrl.iteye.com/blog/1845309

一.JAVA内存模型(JMM,JAVA Memory Model):

运行时涉及到两种内存,主内存和工作区内存,其中工作区内存通常为CPU的高速缓存区用来加快内存数据读取操作的(各线程独立).所有的变量内容都存在主内存中,当需要对内存数据进行操作时,数据将会从主存中load到工作区缓存并由CPU计算和赋值操作,然后再由工作区内存write到主存中,读取时如果工作区内存中已经有(loaded)则直接使用;工作区内存保存了线程使用的变量的副本,线程不可以直接操作主内存,只能操作工作区内存,对于需要变更的变量,需要通过一系列回写指令集同步到主内存中.且工作区内存是线程独占的,主内存是线程共享的.如下为操作集:

  1. lock:对主内存中的变量"加锁",标记为一个线程持有.如果一个变量已经被lock,其他线程尝试lock将被阻塞,同一个线程可以多次lock,不过锁的引用次数将会+1,需要unlock同样次数,才能解锁.对一个变量lock将会导致清空工作区内存中此变量的副本,即当其他线程再次使用此变量时需要重新获取.
  2. unlock:对主内存中的变量"释放锁",释放锁的线程和持有锁的线程必须是同一个线程,无法对没有加锁的变量执行unlock.
  3. read:由工作区内存向主内存发出"read"操作,随后必须执行load操作.
  4. load:工作区内存中加载"read"操作指定的变量,并放入副本中.此指令需要和read保持顺序
  5. use:变量交付给执行引擎做计算,当JVM需要使用变量时,将会使用此操作.
  6. assign:在工作区内存中,将变量更新为某个值.一个被assign操作的变量,必须被write到主内存,如果没有被assign的变量不能被write到主内存.
  7. store:工作区内存向主内存发出"同步"操作.
  8. write:工作区内存将store操作指定的变量同步到主内存中.此操作需要和store保持顺序.

其中read->load,store->write指令必须按照顺序执行,即不能load一个没有被read操作指定的变量,也不能write一个没有被store操作指定的变量,不过这read + load/store + write不一定必须是连续的,其中间仍然可以有其他指令.(volatile有特例)

volatile是java提供的轻量级变量同步机制,它确保了变量可见性,即任何一个线程修改了volatile修饰的变量,其他线程将立即可见.对于普通变量因为存在主内存和工作区内存的复制和同步,所以无法具备此特性.volatile变量存储在主内中,任何线程需要使用此变量时,必须再次read,因为其他线程对此变量的更改,将会被其他线程在使用此变量时立即获得新值.

volatile只是保证了可见性,但是它并非线程安全,因为如果线程一旦read到此值然后进行计算,在尚未write到主内存时其他线程也做了同样的操作,那么volatile变量的最终结果将无法达到预期..如果期望volatile变量线程安全,必须同步或者CAS.volatile变量操作时,read->load->use三个操作是连续的,assign->store->write三个操作是连续的.

通常volatile对“值”类型的对象是有效的,对引用类型是没有意义的。

二.线程安全

    线程是执行任务的最小调度单元,内核线程是OS创建和管理的线程,它将有内核完成线程的切换以及调度(CPU调度).任何一个java线程都对应一个内核线程,即java线程的所有特性都基于内核并受制于内核.在linux和windows系统中,一个java线程就是底层的一个内核线程.java对线程的调度基于内核,在主流的系统中,广泛采用了"抢占式"调度机制,即线程都以"争抢CPU资源"的姿态来运行,最终被运行的线程将有内核的调度算法来决定,如果线程没有获得运行资源,那么线程将被"暂停".."协同式"调度已经不适合多线程(进程)的系统,它表现为线程之间互相"谦让",如果一个线程获得运行资源,那么它将一直运行下去直到结束,如果一个线程是"长时间"的,那么极有可能这个线程将独占一个CPU,而其他线程无法获得资源..

线程状态:

  1. NEW:新创建线程,尚未开启.
  2. RUNNABLE:当前线程已经被启动或者正在被运行,处于此状态的线程标明即将或者已经得到了运行资源.
  3. WAITING:如果线程因为wait()/join()/LockSupport.park(this)/sleep()导致当前线程无法继续执行或者获得资源.
  4. BLOCKED:如果当前线程因为对象锁获取时,被"阻塞",那么线程将处于BLOCKED状态,此状态下,线程不会释放资源.
  5. TERMINATED:线程执行结束,资源即将被回收.

在JAVA中(甚至任何语言或者平台中)确保线程安全的方式,无外乎"同步锁"和"CAS","同步锁"是一种粗暴而严格的同步手段,它强制对资源的访问必须队列化,一个资源在任何时候只能有一个线程可访问.在java中"synchronized"修饰词可以用来同步方法的调用,synchronized可以指定需要同步的对象,如果 没有指定,默认为当前对象,如果是static方法,则表示对Class同步.synchronized关键词在编译之后,最终会生成2个指令:monitorenter和monitorexit,执行引擎如果遇到monitorenter指令,将会尝试获取对象锁,如果获取成功,则锁计数器+1,同时工作区中的对象值将视为无效,重新从主存中load;monitorexit将导致锁计数器-1,即释放锁,此时将会把对象值从工作区缓存中write到主存中;如果计数器为0,则表示此对象没有被任何线程加锁.如果获取锁失败,当前线程阻塞.此外synchronized本身具有"重入性"语义,如果此对象上的monitor是当前线程,那么锁获取操作将直接成功.

我们不再争论synchronized锁和ReentrantLock API锁谁更优秀,这一把双刃剑,性能方面两者在普通情况下(即无复杂递深的lock调用或者多层synchronized)性能几乎差不多,synchronized稍微优秀一些.但是ReentrantLock提供了多样化的控制以及Condition机制,可以帮助我们有效的控制并发环境中,让线程遵循条件的阻塞和唤醒;例如BlockingQueue的实现机制.

CAS(Compare and swap),设计方式上更像一种"乐观锁",通过"比较"-"更新"这种无阻塞的手段实现数据在多线程下的"安全性".在JAVA中CAS操作遍布Atomic包下的API中,底层使用一个闭源的Unsafe.compareAndSwapInt(Object,valueOffset,expect,update),其中需要告知对象的内存地址.CAS会出现一个有趣的问题,就是ABA,即A变量被更改为B之后,再次被更改为A,此时对于持有A数据的线程尝试更改值是可以成功了,就像B值从来就没有出现过一样..其实吧,这个问题不是问题,既然有线程把数据更改为A,那么后续的线程操作就应该遵守现在的结果,而无需关注过去的过程.

【转载】JAVA内存模型和线程安全相关推荐

  1. java线程的优先级是数字越大优先级越高_《深入理解Java虚拟机》5分钟速成:12章(Java内存模型与线程)...

    第12章 Java内存模型与线程 前言: 1.物理机如何处理并发问题? 2.什么是Java内存模型? 3.原子性.可见性.有序性的具体含义和应用实现? 4.volatile 关键字特性? 5.基于vo ...

  2. jvm(12)-java内存模型与线程

    [0]README 0.1)本文部分文字描述转自"深入理解jvm",旨在学习"java内存模型与线程" 的基础知识: [1]概述 1)并发处理的广泛应用是使得 ...

  3. 深入理解Java虚拟机(第三版)-13.Java内存模型与线程

    13.Java内存模型与线程 1.Java内存模型 Java 内存模型的主要目的是定义程序中各种变量的访问规则,即关注在虚拟机中把变量值存储到主内存和从内存中取出变量值的底层细节 该变量指的是 实例字 ...

  4. jvm面试之 Java内存模型之线程独占部分,线程共享部分,常问问题分析

    Java内存模型之线程独占部分 Java内存模型之线程共享部分 Java内存模型之常问问题分析

  5. Java内存模型:线程并发下的对象逸出

    Java内存模型:线程并发下的对象逸出原因分析及解决方案:

  6. java虚拟机-java内存模型与线程

    java内存模型与线程 java内存模型 主内存和工作内存 内存间的交互 volatitle关键字 可见性 禁止指令重排 long和double型变量的特殊规则 原子性,可见性,有序性 先行先发生原则 ...

  7. Java内存模型与线程

    一.一致性 高速缓存的存储交互很好的解决了处理器与内存的速度矛盾,但也存在缓存一致性(cache coherence)问题 二.java内存模型 内存模型:对特定的内存或高速缓存进行读写访问的过程抽象 ...

  8. Java 内存模型与线程

    when ? why ? how ? what ? 计算机的运行速度和它的存储和通信子系统速度的差距太大,大量的时间都花费在磁盘I/O .网络通信或者数据库访问上.如何把处理器的运算能力"压 ...

  9. 深入理解Java虚拟机——第十二章——Java内存模型与线程

    硬件效率与一致性 处理器需要与内存交互,但处理器运算速度与对内存的I/O操作速度相差几个数量级,因此现代操作系统不得不加入尽可能接近处理器运算速度的高速缓存来作为内存与处理器之前的缓冲.这样处理器就不 ...

最新文章

  1. ios11修改微信步数_小程序同步微信步数
  2. 为什么汉字不能当密码,假如用汉字做密码,又会怎样?
  3. 良心直播!看完你的Python入门基础就妥了!
  4. Web开发人员必读的12个网站
  5. chrome使用 postwoman_Postman的情敌,Postwoman了解一下
  6. P1217 回文质数(打表)
  7. rust布料怎么弄_布料“难弄”,你需要从这六方面解决!
  8. opencv轻松入门面向python下载_OpenCV轻松入门:面向Python
  9. 计算机基础 课程简介,课程简介及计算机基础知识课件.ppt
  10. linux nls_lang oracle,linux操作系统环境变量LANG和NLS_LANG的区别
  11. 【读书笔记】Java基础学习之目录
  12. 飓鼎玩笑傲江湖服务器维护,12月24日维护更新公告 - 笑傲江湖12月24日维护更新公告 - 17173笑傲江湖官网合作专区 - 17173.com中国游戏第一门户站...
  13. Proteus 8 中的激励源
  14. 诗词大全给力版_小学生诗词必背75+80首,课内课外全掌握,还送配套练习册amp;视频课...
  15. nominal和ordinal 数据处理中四种基本数据类型
  16. 浪潮存储通过ISCSI映射至Linux服务器、多路径配置方法
  17. BUUCTF中的“新年快乐”
  18. 用css伪元素制作箭头图标
  19. 苹果为M1 MacBook Air/Pro提供自助维修服务 维修成本更低
  20. 阿里工程师修养之:技术三板斧:关于技术规划、管理、架构的思考的概述

热门文章

  1. postgresql数据库备份与还原
  2. 实用 —— PowerCLI (一)
  3. Android笔记: 解决ScrollView嵌套ListView的冲突方法
  4. java --HashTable学习
  5. 数码显示实验报告C语言,数码问题C语言A星算法详细实验报告含代码(9页)-原创力文档...
  6. springboot创建单个对象
  7. 多案分库分表带来的问题-分布式事务
  8. ReentrantLock重入锁
  9. SpringMVC拦截器-interceptor和filter区别
  10. SpringCloud:Ribbon负载均衡(基本使用、 负载均衡、自定义配置、禁用 Eureka 实现 Ribbon 调用)