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

Java內(nèi)存模型原子性原理及實(shí)例解析

 更新時(shí)間:2019年12月23日 14:53:30   作者:寫代碼的木公  
這篇文章主要介紹了Java內(nèi)存模型原子性原理及實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

這篇文章主要介紹了Java內(nèi)存模型原子性原理及實(shí)例解析,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

本文就具體來講講JMM是如何保證共享變量訪問的原子性的。

原子性問題

原子性是指:一個(gè)或多個(gè)操作,要么全部執(zhí)行且在執(zhí)行過程中不被任何因素打斷,要么全部不執(zhí)行。

下面就是一段會(huì)出現(xiàn)原子性問題的代碼:

public class AtomicProblem {

  private static Logger logger = LoggerFactory.getLogger(AtomicProblem.class);
  public static final int THREAD_COUNT = 10;

  public static void main(String[] args) throws Exception {
    BankAccount sharedAccount = new BankAccount("account-csx",0.00);
    ArrayList<Thread> threads = new ArrayList<>();
    for (int i = 0; i < THREAD_COUNT; i++) {
      Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
          for (int j = 0; j < 1000 ; j++) {
            sharedAccount.deposit(10.00);
          }
        }
      });
      thread.start();
      threads.add(thread);
    }
    for (Thread thread : threads) {
      thread.join();
    }
    logger.info("the balance is:{}",sharedAccount.getBalance());
  }


  public static class BankAccount {
    private String accountName;

    public double getBalance() {
      return balance;
    }

    private double balance;

    public BankAccount(String accountName, double balance){
      this.accountName = accountName;
      this.balance =balance;
    }
    public double deposit(double amount){
      balance = balance + amount;
      return balance;
    }
    public double withdraw(double amount){
      balance = balance - amount;
      return balance;
    }
    public String getAccountName() {
      return accountName;
    }
    public void setAccountName(String accountName) {
      this.accountName = accountName;
    }
  }
}

上面的代碼中開啟了10個(gè)線程,每個(gè)線程會(huì)對(duì)共享的銀行賬戶進(jìn)行1000次存款操作,每次存款10塊,所以理論上最后銀行賬戶中的錢應(yīng)該是10 * 1000 * 10 = 100000塊。我執(zhí)行了多次上面的代碼,很多次最后的結(jié)果的確是100000,但是也有幾次的結(jié)果并不是我們預(yù)期的。

14:40:25.981 [main] INFO com.csx.demo.spring.boot.concurrent.jmm.AtomicProblem - the balance is:98260.0

出現(xiàn)上面結(jié)果的原因就是因?yàn)橄旅娴牟僮鞑⒉皇窃硬僮?,其中的balance是一個(gè)共享變量。在多線程環(huán)境下可能會(huì)被打斷。

balance = balance + amount;

上面的賦值操作被分為多步執(zhí)行完成,下面簡單解析下兩個(gè)線程對(duì)balance同時(shí)加10的過程(模擬存款過程,假設(shè)balance的初始值還是0)

線程1從共享內(nèi)存中加載balance的初始值0到工作內(nèi)存
線程1對(duì)工作內(nèi)存中的值加10

//此時(shí)線程1的CPU時(shí)間耗盡,線程2獲得執(zhí)行機(jī)會(huì)

線程2從共享內(nèi)存中加載balance的初始值到工作內(nèi)存,此時(shí)balance的值還是0
線程2對(duì)工作內(nèi)存中的值加10,此時(shí)線程2工作內(nèi)存中的副本值是10
線程2將balance的副本值刷新回共享內(nèi)存,此時(shí)共享內(nèi)存中balance的值是10

//線程2CPU時(shí)間片耗盡,線程1又獲得執(zhí)行機(jī)會(huì)
線程1將工作內(nèi)存中的副本值刷新回共享內(nèi)存,但是此時(shí)副本的值還是10,所以最后共享內(nèi)存中的值也是10

上面簡單模擬了一個(gè)原子性問題導(dǎo)致程序最終結(jié)果出錯(cuò)的過程。

JMM對(duì)原子性問題的保證

自帶原子性保證

在Java中,對(duì)基本數(shù)據(jù)類型的變量的讀取和賦值操作是原子性操作。

a = true; //原子性
a = 5;   //原子性
a = b;   //非原子性,分兩步完成,第一步加載b的值,第二步將b賦值給a
a = b + 2; //非原子性,分三步完成
a ++;   //非原子性,分三步完成

synchronized

synchronized可以保證操作結(jié)果的原子性。synchronized保證原子性的原理也很簡單,因?yàn)閟ynchronized可以防止多個(gè)線程并發(fā)執(zhí)行一段代碼。還是用上面存款的場景做列子,我們只需要將存款的方法設(shè)置成synchronized的就能保證原子性了。

 public synchronized double deposit(double amount){
   balance = balance + amount; //1
   return balance;
 }

加了synchronized后,當(dāng)一個(gè)線程沒執(zhí)行完deposit這個(gè)方法前,其他線程是不能執(zhí)行這段代碼的。其實(shí)我們發(fā)現(xiàn)synchronized并不能將上面的代碼1編程原子性操作,上面的代碼1還是有可能被中斷的,但是即使被中斷了其他線程也不能訪問共享變量balance,當(dāng)之前被中斷的線程繼續(xù)執(zhí)行時(shí)得到的結(jié)果還是正確的。

因此synchronized對(duì)原子性問題的保證是從最終結(jié)果上來保證的,也就是說它只保證最終的結(jié)果正確,中間操作的是否被打斷沒法保證。這個(gè)和CAS操作需要對(duì)比著看。

Lock鎖

public double deposit(double amount) {
  readWriteLock.writeLock().lock();
  try {
    balance = balance + amount;
    return balance;
  } finally {
    readWriteLock.writeLock().unlock();
  }
}

Lock鎖保證原子性的原理和synchronized類似,這邊不進(jìn)行贅述了。

原子操作類型

public static class BankAccount {
  //省略其他代碼
  private AtomicDouble balance;

  public double deposit(double amount) {
    return balance.addAndGet(amount);
  }
  //省略其他代碼
} 

JDK提供了很多原子操作類來保證操作的原子性。原子操作類的底層是使用CAS機(jī)制的,這個(gè)機(jī)制對(duì)原子性的保證和synchronized有本質(zhì)的區(qū)別。CAS機(jī)制保證了整個(gè)賦值操作是原子的不能被打斷的,而synchronized值能保證代碼最后執(zhí)行結(jié)果的正確性,也就是說synchronized能消除原子性問題對(duì)代碼最后執(zhí)行結(jié)果的影響。

簡單總結(jié)

在多線程編程環(huán)境下(無論是多核CPU還是單核CPU),對(duì)共享變量的訪問存在原子性問題。這個(gè)問題可能會(huì)導(dǎo)致程序錯(cuò)誤的執(zhí)行結(jié)果。JMM主要提供了如下的方式來保證操作的原子,保證程序不受原子性問題的影響。

  • synchronized機(jī)制:保證程序最終正確性,是的程序不受原子性問題的影響;
  • Lock接口:和synchronized類似;
  • 原子操作類:底層使用CAS機(jī)制,能保證操作真正的原子性。

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • java定義二維數(shù)組的幾種寫法(小結(jié))

    java定義二維數(shù)組的幾種寫法(小結(jié))

    下面小編就為大家?guī)硪黄猨ava定義二維數(shù)組的幾種寫法(小結(jié))。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2016-10-10
  • Java中BigDecimal類的add()的使用詳解

    Java中BigDecimal類的add()的使用詳解

    這篇文章主要介紹了Java中BigDecimal類的add()的使用詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • Spring實(shí)現(xiàn)HikariCP連接池的示例代碼

    Spring實(shí)現(xiàn)HikariCP連接池的示例代碼

    在SpringBoot 2.0中,我們使用默認(rèn)連接池是HikariCP,本文講一下HikariCP的具體使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2021-08-08
  • Springboot工具類FileCopyUtils使用教程

    Springboot工具類FileCopyUtils使用教程

    這篇文章主要介紹了Springboot內(nèi)置的工具類之FileCopyUtils的使用,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧
    2022-12-12
  • Presto自定義函數(shù)@SqlNullable引發(fā)問題詳解

    Presto自定義函數(shù)@SqlNullable引發(fā)問題詳解

    這篇文章主要為大家介紹了Presto自定義函數(shù)@SqlNullable引發(fā)問題詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-12-12
  • spring?boot前后端交互之?dāng)?shù)據(jù)格式轉(zhuǎn)換問題

    spring?boot前后端交互之?dāng)?shù)據(jù)格式轉(zhuǎn)換問題

    這篇文章主要介紹了spring?boot前后端交互之?dāng)?shù)據(jù)格式轉(zhuǎn)換,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-01-01
  • java 對(duì)稱二叉樹的判斷

    java 對(duì)稱二叉樹的判斷

    這篇文章主要介紹了java 對(duì)稱二叉樹的判斷,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • 解讀@EventListener工作原理

    解讀@EventListener工作原理

    這篇文章主要介紹了@EventListener工作原理,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java 使用反射調(diào)用jar包中的類方式

    Java 使用反射調(diào)用jar包中的類方式

    這篇文章主要介紹了Java 使用反射調(diào)用jar包中的類方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 一文帶你深入解析Java應(yīng)用線程轉(zhuǎn)儲(chǔ)

    一文帶你深入解析Java應(yīng)用線程轉(zhuǎn)儲(chǔ)

    在Java應(yīng)用運(yùn)維和問題排查過程中,線程轉(zhuǎn)儲(chǔ)(Thread?Dump)是一個(gè)非常重要的工具,它能夠幫助我們了解JVM內(nèi)部線程的運(yùn)行狀態(tài),下面小編來和大家詳細(xì)介紹一下它吧
    2025-04-04

最新評(píng)論

渝北区| 金沙县| 美姑县| 东山县| 保亭| 衡水市| 全南县| 即墨市| 宁夏| 太湖县| 根河市| 基隆市| 红原县| 涪陵区| 松桃| 阿拉善右旗| 临澧县| 县级市| 哈尔滨市| 乐平市| 洛川县| 奉节县| 哈密市| 讷河市| 沧州市| 莱芜市| 民乐县| 车致| 鹤山市| 普兰县| 禄劝| 津南区| 新乐市| 从化市| 桃源县| 迁安市| 天柱县| 同江市| 敦煌市| 那坡县| 龙江县|