最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java 存儲(chǔ)模型和共享對(duì)象詳解

 更新時(shí)間:2017年03月02日 11:50:37   投稿:lqh  
這篇文章主要介紹了Java 存儲(chǔ)模型和共享對(duì)象詳解的相關(guān)資料,對(duì)Java存儲(chǔ)模型,可見性和安全發(fā)布的問題是起源于Java的存儲(chǔ)結(jié)構(gòu)及共享對(duì)象安全,需要的朋友可以參考下

Java 存儲(chǔ)模型和共享對(duì)象詳解

很多程序員對(duì)一個(gè)共享變量初始化要注意可見性和安全發(fā)布(安全地構(gòu)建一個(gè)對(duì)象,并其他線程能正確訪問)等問題不是很理解,認(rèn)為Java是一個(gè)屏蔽內(nèi)存細(xì)節(jié)的平臺(tái),連對(duì)象回收都不需要關(guān)心,因此談到可見性和安全發(fā)布大多不知所云。其實(shí)關(guān)鍵在于對(duì)Java存儲(chǔ)模型,可見性和安全發(fā)布的問題是起源于Java的存儲(chǔ)結(jié)構(gòu)。

Java存儲(chǔ)模型原理

有很多書和文章都講解過Java存儲(chǔ)模型,其中一個(gè)圖很清晰地說明了其存儲(chǔ)結(jié)構(gòu):

由上圖可知, jvm系統(tǒng)中存在一個(gè)主內(nèi)存(Main Memory或Java Heap Memory),Java中所有變量都儲(chǔ)存在主存中,對(duì)于所有線程都是共享的。 每條線程都有自己的工作內(nèi)存(Working Memory),工作內(nèi)存中保存的是主存中某些變量的拷貝,線程對(duì)所有變量的操作都是在工作內(nèi)存中進(jìn)行,線程之間無法相互直接訪問,變量傳遞均需要通過主存完成。

這個(gè)存儲(chǔ)模型很像我們常用的緩存與數(shù)據(jù)庫(kù)的關(guān)系,因此由此可以推斷JVM如此設(shè)計(jì)應(yīng)該是為了提升性能,提高多線程的并發(fā)能力,并減少線程之間的影響。

Java存儲(chǔ)模型潛在的問題

一談到緩存, 我們立馬想到會(huì)有緩存不一致性問題,就是說當(dāng)有緩存與數(shù)據(jù)庫(kù)不一致的時(shí)候,就需要有相應(yīng)的機(jī)制去同步數(shù)據(jù)。同理,Java存儲(chǔ)模型也有這個(gè)問題,當(dāng)一個(gè)線程在自己工作內(nèi)存里初始化一個(gè)變量,當(dāng)還沒來得及同步到主存里時(shí),如果有其他線程來訪問它,就會(huì)出現(xiàn)不可預(yù)知的問題。另外,JVM在底層設(shè)計(jì)上,對(duì)與那些沒有同步到主存里的變量,可能會(huì)以不一樣的操作順序來執(zhí)行指令,舉個(gè)實(shí)際的例子:

public class PossibleReordering { 
  static int x = 0, y = 0; 
  static int a = 0, b = 0; 
  public static void main(String[] args) 
      throws InterruptedException { 
    Thread one = new Thread(new Runnable() { 
      public void run() { 
        a = 1; 
        x = b; 
      } 
    }); 
    Thread other = new Thread(new Runnable() { 
      public void run() { 
        b = 1; 
        y = a; 
      } 
    }); 
    one.start(); other.start(); 
    one.join();  other.join(); 
    System.out.println("( "+ x + "," + y + ")"); 
  } 
} 

由于,變量x,y,a,b沒有安全發(fā)布,導(dǎo)致會(huì)不以規(guī)定的操作順序來執(zhí)行這次四次賦值操作,有可能出現(xiàn)以下順序:

出現(xiàn)這個(gè)問題也可以理解,因?yàn)榧热贿@些對(duì)象不可見,也就是說本應(yīng)該隔離在各個(gè)線程的工作區(qū)內(nèi),那么對(duì)于有些無關(guān)順序的指令,打亂順序執(zhí)行在JVM看來也是可行的。

因此,總結(jié)起來,會(huì)有以下兩種潛在問題:

  1. 緩存不一致性
  2. 重排序執(zhí)行

解決Java存儲(chǔ)模型潛在的問題

為了能讓開發(fā)人員安全正確地在Java存儲(chǔ)模型上編程,JVM提供了一個(gè)happens-before原則,有人整理得非常好,我摘抄如下:

  1. 在程序順序中, 線程中的每一個(gè)操作, 發(fā)生在當(dāng)前操作后面將要出現(xiàn)的每一個(gè)操作之前.
  2. 對(duì)象監(jiān)視器的解鎖發(fā)生在等待獲取對(duì)象鎖的線程之前.
  3. 對(duì)volitile關(guān)鍵字修飾的變量寫入操作, 發(fā)生在對(duì)該變量的讀取之前.
  4. 對(duì)一個(gè)線程的 Thread.start() 調(diào)用 發(fā)生在啟動(dòng)的線程中的所有操作之前.
  5. 線程中的所有操作 發(fā)生在從這個(gè)線程的 Thread.join()成功返回的所有其他線程之前.

有了原則還不夠,Java提供了以下工具和方法來保證變量的可見性和安全發(fā)布:

  1. 使用 synchronized來同步變量初始化。此方式會(huì)立馬把工作內(nèi)存中的變量同步到主內(nèi)存中
  2. 使用 volatile關(guān)鍵字來標(biāo)示變量。此方式會(huì)直接把變量存在主存中而不是工作內(nèi)存中
  3. final變量。常量?jī)?nèi)也是存于主存中

另外,一定要明確只有共享變量才會(huì)有以上那些問題,如果變量只是這個(gè)線程自己使用,就不用擔(dān)心那么多問題了
搞清楚Java存儲(chǔ)模型后,再來看共享對(duì)象可見性和安全發(fā)布的問題就較為容易了

共享對(duì)象的可見性

當(dāng)對(duì)象在從工作內(nèi)存同步到主內(nèi)存之前,那么它就是不可見的。若有其他線程在存取不可見對(duì)象就會(huì)引發(fā)可見性問題,看下面一個(gè)例子:

public class NoVisibility { 
  private static boolean ready; 
  private static int number; 
  private static class ReaderThread extends Thread { 
    public void run() { 
      while (!ready) 
        Thread.yield(); 
      System.out.println(number); 
    } 
  } 
  public static void main(String[] args) { 
    new ReaderThread().start(); 
    number = 42; 
    ready = true; 
  } 
} 

按照正常邏輯,應(yīng)該會(huì)輸出42,但其實(shí)際結(jié)果會(huì)非常奇怪,可能會(huì)永遠(yuǎn)沒有輸出(因?yàn)閞eady為false),可能會(huì)輸出0(因?yàn)橹嘏判騿栴}導(dǎo)致ready=true先執(zhí)行)。再舉一個(gè)更為常見的例子,大家都喜歡用只有set和get方法的pojo來設(shè)計(jì)領(lǐng)域模型,如下所示:

@NotThreadSafe 
public class MutableInteger { 
  private int value; 
  public int get() { return value; } 
  public void set(int value) { this.value = value; } 
} 

但是,當(dāng)有多個(gè)線程同時(shí)來存取某一個(gè)對(duì)象時(shí),可能就會(huì)有類似的可見性問題。
為了保證變量的可見性,一般可以用鎖、 synchronized關(guān)鍵字、 volatile關(guān)鍵字或直接設(shè)置為final

共享變量發(fā)布

共享變量發(fā)布和我們常說的發(fā)布程序類似,就是說讓本屬于內(nèi)部的一個(gè)變量變?yōu)橐粋€(gè)可以被外部訪問的變量。發(fā)布方式分為以下幾種:

  • 將對(duì)象引用存儲(chǔ)到公共靜態(tài)域
  • 初始化一個(gè)可以被外部訪問的對(duì)象
  • 將對(duì)象引用存儲(chǔ)到一個(gè)集合里

安全發(fā)布和保證可見性的方法類似,就是要同步發(fā)布動(dòng)作,并使發(fā)布后的對(duì)象可見。

線程安全

其實(shí)當(dāng)我們把這些變量封閉在本線程內(nèi)訪問,就可以從根本上避免以上問題,現(xiàn)實(shí)中存在很多例子通過線程封閉來安全使用本不是線程安全的對(duì)象,比如:

  1. swing的可視化組件和數(shù)據(jù)模型對(duì)象并不是線程安全的,它通過將它們限制到swing的事件分發(fā)線程中,實(shí)現(xiàn)線程安全
  2. JDBC Connection對(duì)象沒有要求為線程安全,但JDBC的存取模式?jīng)Q定了一個(gè)Connection只會(huì)同時(shí)被一個(gè)線程使用
  3. ThreadLocal把變量限制在本線程中共享

感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!

相關(guān)文章

  • java開發(fā)中嵌套類的詳解及實(shí)例

    java開發(fā)中嵌套類的詳解及實(shí)例

    這篇文章主要介紹了 java開發(fā)中嵌套類的詳解及實(shí)例的相關(guān)資料,一般把定義內(nèi)部類的外圍類成為包裝類(enclosing class)或者外部類,需要的朋友可以參考下
    2017-07-07
  • Java數(shù)組的定義、初始化、及二維數(shù)組用法分析

    Java數(shù)組的定義、初始化、及二維數(shù)組用法分析

    這篇文章主要介紹了Java數(shù)組的定義、初始化、及二維數(shù)組用法,結(jié)合具體實(shí)例形式分析了java數(shù)組概念、功能、數(shù)組定義、靜態(tài)數(shù)組、動(dòng)態(tài)數(shù)組、二維數(shù)組等相關(guān)使用技巧,需要的朋友可以參考下
    2019-01-01
  • Spring存儲(chǔ)與讀取Bean對(duì)象方法

    Spring存儲(chǔ)與讀取Bean對(duì)象方法

    在Spring中,要想更簡(jiǎn)單的存儲(chǔ)和讀取對(duì)象的核心是使用注解,這篇文章主要給大家介紹了關(guān)于Spring如何通過注解存儲(chǔ)和讀取對(duì)象的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-01-01
  • SpringBoot+MySQL實(shí)現(xiàn)讀寫分離的多種具體方案

    SpringBoot+MySQL實(shí)現(xiàn)讀寫分離的多種具體方案

    在高并發(fā)和大數(shù)據(jù)量的場(chǎng)景下,數(shù)據(jù)庫(kù)成為了系統(tǒng)的瓶頸。為了提高數(shù)據(jù)庫(kù)的處理能力和性能,讀寫分離成為了一種常用的解決方案,本文將介紹在Spring?Boot項(xiàng)目中實(shí)現(xiàn)MySQL數(shù)據(jù)庫(kù)讀寫分離的多種具體方案,需要的朋友可以參考下
    2023-06-06
  • Mybatis動(dòng)態(tài)sql超詳細(xì)講解

    Mybatis動(dòng)態(tài)sql超詳細(xì)講解

    動(dòng)態(tài)SQL是MyBatis的強(qiáng)大特性之一,顧名思義就是會(huì)動(dòng)的SQL,即是能夠靈活的根據(jù)某種條件拼接出完整的SQL語(yǔ)句,下面這篇文章主要給大家介紹了關(guān)于Mybatis動(dòng)態(tài)sql的相關(guān)資料,需要的朋友可以參考下
    2023-04-04
  • 通過Java查看程序資源占用情況

    通過Java查看程序資源占用情況

    這篇文章主要介紹了通過Java查看程序資源占用情況,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-05-05
  • java 代理機(jī)制的實(shí)例詳解

    java 代理機(jī)制的實(shí)例詳解

    這篇文章主要介紹了java 代理機(jī)制的實(shí)例詳解的相關(guān)資料,這里說明下如何實(shí)現(xiàn)代理機(jī)制,幫助大家理解掌握這部分內(nèi)容,需要的朋友可以參考下
    2017-08-08
  • Java如何使用Set接口存儲(chǔ)沒有重復(fù)元素的數(shù)組

    Java如何使用Set接口存儲(chǔ)沒有重復(fù)元素的數(shù)組

    Set是一個(gè)繼承于Collection的接口,即Set也是集合中的一種。Set是沒有重復(fù)元素的集合,本篇我們就用它存儲(chǔ)一個(gè)沒有重復(fù)元素的數(shù)組
    2022-04-04
  • 詳解SpringBoot?統(tǒng)一后端返回格式的方法

    詳解SpringBoot?統(tǒng)一后端返回格式的方法

    今天我們來聊一聊在基于SpringBoot前后端分離開發(fā)模式下,如何友好的返回統(tǒng)一的標(biāo)準(zhǔn)格式以及如何優(yōu)雅的處理全局異常,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2022-05-05
  • Java工程中可執(zhí)行JAR兩種打包方式詳解

    Java工程中可執(zhí)行JAR兩種打包方式詳解

    這篇文章主要為大家詳細(xì)介紹了Java工程中可執(zhí)行JAR兩種打包方式,一體化可執(zhí)行包和帶外部依賴lib的可執(zhí)行包,有需要的小伙伴可以學(xué)習(xí)一下
    2024-04-04

最新評(píng)論

田东县| 洞口县| 德格县| 六安市| 涿州市| 仁怀市| 简阳市| 佛学| 安阳县| 平塘县| 兴安盟| 万年县| 中江县| 乌拉特后旗| 利川市| 志丹县| 乌拉特后旗| 乌兰县| 兴化市| 磐石市| 诏安县| 武邑县| 大同市| 抚州市| 上栗县| 永靖县| 紫云| 元阳县| 沧州市| 互助| 林周县| 敦化市| 渭南市| 高平市| 大姚县| 新乡县| 聂拉木县| 三门县| 建瓯市| 象山县| 古交市|