jvm内存模型

一、Java内存模型定义

        Java内存模型规定了所有的变量都存储在主内存,每条线程还有自己的工作内存中保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作(读取、赋值等),都必须在工作内存中进行,而不能直接读写主内存中的变量。不同的线程之间无法直接访问对方工作内存中的变量,线程间变量值的传递均需要通过主内存来完成,三者关系的交互如图所示:

java memory model

      1.1、单线程、本地内存、主内存是如何进行变量的数据交互?

                内存模型JMM定义了8中操作来完成三者之间的数据交互,虚拟机保证每一种操作都是原子性的。

            Lock:作用于主内存的变量,它把一个变量标识为线程独占状态

            Unlock:作用于主内存的变量,将一个处于锁定状态下的变量释放出来,释放后的变量才可以被其他线程加锁。

            Read:作用于主内存的变量,将一个变量的值从主内存传输到线程的工作内存。

            Load:作用于工作内存的变量,将read操作得到的变量的值放入工作内存中的变量副本中

            Use:作用于工作内存的变量,将工作内存中变量的值传递给执行引擎,当虚拟机需要使用时会执行这个操作。

            Assign:作用于工作内存的变量,将一个执行引擎接收到的值赋给工作内存中的变量,当虚拟机遇到给变量赋值的字节码时会执行此操作。

            Store:将工作内存中的一个变量值传送到主内存中

            Write:作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

      1.2、线程A与线程B之间如何进行变量的数据交互?

                1.2.1、 首先,线程A把本地内存A中更新过的共享变量刷新到主内存中去。

                1.2.2、 然后,线程B到主内存中去读取线程A之前已更新过的共享变量。


二、不得不提的基础概念:

        2.1、内存屏障

            内存屏障是一个CPU指令,它的作用是:1、当CPU处理指令时,插入一条Memory Barrier会告诉编译器和CPU:不管什么指令都不能和这条Memory Barrier指令重排序;2、Memory Barrier所做的另外一件事是强制刷出各种CPU cache,确保变量的内存可见性。

            例子如:Write-Barrier将刷出在Barrier之前写入到本地内存的数据,更新数据到主存中;Read-Barrier将主存中最新数据刷出到每个线程的本地内存中

      2.2、指令重排序

            指令排序是:编译器或运行时环境为了优化程序性能而采取的对指令进行重新排序执行的一种手段。

            指令重排序的目的是提高程序的运行并发度,发生在编译器、runtime和处理器阶段,遵循as-if-serial语义(不管怎么重排序,单线程程序的执行结果不能改变),也就是重排序所带来的问题是针对多线程的。

            重排序发生的条件是A和B没有存在依赖关系,这里的依赖关系是指“数据依赖关系”和“控制依赖关系”两种。其中数据依赖表示两个以上操作访问同一个变量,且这两个操作中有一个为写操作。而控制依赖关系,比如if(a>0){int i = a*a;}。

        2.3、变量原子性

          访问基本类型的字段的值,或是对其更新操作的时候,除开long类型和double类型,其他类型字段的程序中操作,都将具有原子性操作。例子如:int num=0; 原子性,int numA = numB,[同时存在访问和更新操作],非原子性。

      2.4、变量可见性

            当多个线程同时访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。


三、根据以上几个基础概念,解析并发编程中的几个关键字

    3.1、volatile(禁止重排序、变量可见性)

            使用volatile关键字时,在读取变量时,会在读取之前多出一个Read-Barrier指令;在写入变量时,会在写入之后多出一个Write-Barrier指令,保证了变量的可见。它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面。

            例子如:

volatile使用

        可能有人这段程序的输出结果小于10000,感到惊讶。但事实的确会如此。

        volatile关键字能保证可见性,但是上面的程序错在没能保证原子性。可见性只能保证每次读取的是最新的值,但是volatile没办法保证对变量的操作的原子性。

  自增操作是不具备原子性的,它包括读取变量的原始值、进行加1操作、写入工作内存。那么就是说自增操作的三个子操作可能会分割开执行,就有可能导致下面这种情况出现:

  假如某个时刻变量inc的值为10,

  线程1对变量进行自增操作,线程1先读取了变量inc的原始值,然后线程1被阻塞了;

  然后线程2对变量进行自增操作,线程2也去读取变量inc的原始值,由于线程1只是对变量inc进行读取操作,而没有对变量进行修改操作,所以不会导致线程2的工作内存中缓存变量inc的缓存行无效,所以线程2会直接去主存读取inc的值,发现inc的值时10,然后进行加1操作,并把11写入工作内存,最后写入主存。

  然后线程1接着进行加1操作,由于已经读取了inc的值,注意此时在线程1的工作内存中inc的值仍然为10,所以线程1对inc进行加1操作后inc的值为11,然后将11写入工作内存,最后写入主存。

  那么两个线程分别进行了一次自增操作后,inc只增加了1。

    3.2、synchronized(禁止重排序、变量可见性、变量原子性)

        synchronized只是一个内置锁的加锁机制,当某个方法或代码块加上synchronized关键字后,就表明要获得该内置锁才能执行,并不能阻止其他线程访问不需要获得该内置锁的方法。

        java的内置锁:每个java对象都可以用做一个实现同步的锁,这些锁成为内置锁。线程进入同步代码块或方法的时候会自动获得该锁,在退出同步代码块或方法时会释放该锁。

        java内置锁是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或者阻塞,知道线程B释放这个锁,如果B线程不释放这个锁,那么A线程将永远等待下去。

        java的对象锁和类锁:java的对象锁和类锁在锁的概念上基本上和内置锁是一致的,但是,两个锁实际是有很大的区别的,对象锁是用于对象实例方法,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的。我们知道,类的对象实例可以有很多个,但是每个类只有一个class对象,所以不同对象实例的对象锁是互不干扰的,但是每个类只有一个类锁。但是有一点必须注意的是,其实类锁只是一个概念上的东西,并不是真实存在的,它只是用来帮助我们理解锁定实例方法和静态方法的区别的。更深入理解synchronized关键字传送门!

    3.3、ThreadLocal

        ThreadLocal并非一个线程,而是一个线程局部变量。它的作用就是为使用该变量的线程都提供一个变量值的副本,每个线程都可以独立的改变自己的副本,而不会和其他线程的副本造成冲突。

        ThreadLocal是解决线程安全问题一个很好的思路,ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本,由于Key值不可重复,每一个“线程对象”对应线程的“变量副本”,而到达了线程安全。

        概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。更深入理解ThreadLocal关键词传送门!


        我是先生,找寻着那位迷路的Miss。最后,愿各位javaer,合上电脑的刹那,有着侠客收剑入鞘的骄傲!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,362评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,330评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,247评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,560评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,580评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,569评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,929评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,587评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,840评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,596评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,678评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,366评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,945评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,929评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,165评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,271评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,403评论 2 342

推荐阅读更多精彩内容

  • 2017红红火火,为什么选它,因为它画起来真心省事。算是画的比较成功的一次喔。继续磨呀磨呀磨下去,相信会更好。
    晨星手绘阅读 449评论 7 9
  • 今天情人节,请媳妇儿一起看了获奖无数的电影《爱乐之城》。很应景。媳妇儿看哭了,虽然直呼没看懂;我眼眶没红,但感触颇...
    格桑菩提子阅读 334评论 0 0
  • 为了迎接省里领导视察,我们在检察室准备了一大间办公室,叫做“八部门联合办公室”即八个业务部门在这里联合办公。 那天...
    高慧_阅读 338评论 0 1
  • 人们往往把《孙子兵法》和三十六计合成一本书,那是因为我们把《孙子兵法》里的第一个字就读偏了,这是价值观的偏差,三十...
    astak3阅读 1,485评论 3 1