<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          DCL(單例雙重檢查鎖模式)詳解

          共 3117字,需瀏覽 7分鐘

           ·

          2021-04-08 12:14

          先看一下DCL(雙重檢查鎖模式)的示例代碼:

          public class Singleton {

          //Singleton對(duì)象屬性,加上volatile關(guān)鍵字是為了防止指定重排序,要知道singleton = new Singleton()拆分成cpu指令的話,有足足3個(gè)步驟
          private volatile static Singleton singleton;

          //對(duì)外提供的獲取實(shí)例的方法
          public static Singleton getInstance() {
          if (singleton == null) {
          synchronized (Singleton.class) {
          if (singleton == null) {
          singleton = new Singleton();
          }
          }
          }
          return singleton;
          }
          }

          從代碼里可以看到,做了兩重的singleton == null的判斷,中間還用了synchronized關(guān)鍵字,第一個(gè)singleton == null的判斷是為了避免線程串行化,如果為空,就進(jìn)入synchronized代碼塊中,獲取鎖后再操作,如果不為空,直接就返回singleton對(duì)象了,無(wú)需再進(jìn)行鎖競(jìng)爭(zhēng)和等待了。而第二個(gè)singleton == null的判斷是為了防止有多個(gè)線程同時(shí)跳過(guò)第一個(gè)singleton == null的判斷,比如線程一先獲取到鎖,進(jìn)入同步代碼塊中,發(fā)現(xiàn)singleton實(shí)例還是null,就會(huì)做new操作,然后退出同步代碼塊并釋放鎖,這時(shí)一起跳過(guò)第一層singleton == null的判斷的還有線程二,這時(shí)線程一釋放了鎖,線程二就會(huì)獲取到鎖,如果沒有第二層的singleton == null這個(gè)判斷擋著,那就會(huì)再創(chuàng)建一個(gè)singleton實(shí)例,就違反了單例的約束了。

          那為什么要加volatile關(guān)鍵字呢

          了解下singleton = new Singleton()這段代碼其實(shí)不是原子性的操作,它至少分為以下3個(gè)步驟:

          1. 給singleton對(duì)象分配內(nèi)存空間

          2. 調(diào)用Singleton類的構(gòu)造函數(shù)等,初始化singleton對(duì)象

          3. 將singleton對(duì)象指向分配的內(nèi)存空間,這步一旦執(zhí)行了,那singleton對(duì)象就不等于null了

          這里還需要知道一點(diǎn),就是有時(shí)候JVM會(huì)為了優(yōu)化,而做指令重排序的操作,這里的指令,指的是CPU層面的。

          正常情況下,singleton = new Singleton()的步驟是按照1->2->3這種步驟進(jìn)行的,但是一旦JVM做了指令重排序,那么順序很可能編程1->3->2,如果是這種順序,可以發(fā)現(xiàn),在3步驟執(zhí)行完singleton對(duì)象就不等于null,但是它其實(shí)還沒做步驟二的初始化工作,但是另一個(gè)線程進(jìn)來(lái)時(shí)發(fā)現(xiàn),singleton不等于null了,就這樣把半成品的實(shí)例返回去,調(diào)用是會(huì)報(bào)錯(cuò)的。

          可以畫個(gè)出現(xiàn)指令重排序的圖加深下理解:

          出現(xiàn)了指令重排序后,按照上圖的流程邏輯,很可能會(huì)返回還沒完成初始化的singleton對(duì)象,導(dǎo)致使用這個(gè)對(duì)象時(shí)報(bào)錯(cuò),而volatile關(guān)鍵字的作用之一就是禁止指令重排序。

          總結(jié)

          DCL使用volatile關(guān)鍵字,是為了禁止指令重排序,避免返回還沒完成初始化的singleton對(duì)象,導(dǎo)致調(diào)用報(bào)錯(cuò),也保證了線程的安全。

          先看一下DCL(雙重檢查鎖模式)的示例代碼:

          public class Singleton {

          //Singleton對(duì)象屬性,加上volatile關(guān)鍵字是為了防止指定重排序,要知道singleton = new Singleton()拆分成cpu指令的話,有足足3個(gè)步驟
          private volatile static Singleton singleton;

          //對(duì)外提供的獲取實(shí)例的方法
          public static Singleton getInstance() {
          if (singleton == null) {
          synchronized (Singleton.class) {
          if (singleton == null) {
          singleton = new Singleton();
          }
          }
          }
          return singleton;
          }
          }

          從代碼里可以看到,做了兩重的singleton == null的判斷,中間還用了synchronized關(guān)鍵字,第一個(gè)singleton == null的判斷是為了避免線程串行化,如果為空,就進(jìn)入synchronized代碼塊中,獲取鎖后再操作,如果不為空,直接就返回singleton對(duì)象了,無(wú)需再進(jìn)行鎖競(jìng)爭(zhēng)和等待了。而第二個(gè)singleton == null的判斷是為了防止有多個(gè)線程同時(shí)跳過(guò)第一個(gè)singleton == null的判斷,比如線程一先獲取到鎖,進(jìn)入同步代碼塊中,發(fā)現(xiàn)singleton實(shí)例還是null,就會(huì)做new操作,然后退出同步代碼塊并釋放鎖,這時(shí)一起跳過(guò)第一層singleton == null的判斷的還有線程二,這時(shí)線程一釋放了鎖,線程二就會(huì)獲取到鎖,如果沒有第二層的singleton == null這個(gè)判斷擋著,那就會(huì)再創(chuàng)建一個(gè)singleton實(shí)例,就違反了單例的約束了。

          那為什么要加volatile關(guān)鍵字呢

          了解下singleton = new Singleton()這段代碼其實(shí)不是原子性的操作,它至少分為以下3個(gè)步驟:

          1. 給singleton對(duì)象分配內(nèi)存空間

          2. 調(diào)用Singleton類的構(gòu)造函數(shù)等,初始化singleton對(duì)象

          3. 將singleton對(duì)象指向分配的內(nèi)存空間,這步一旦執(zhí)行了,那singleton對(duì)象就不等于null了

          這里還需要知道一點(diǎn),就是有時(shí)候JVM會(huì)為了優(yōu)化,而做指令重排序的操作,這里的指令,指的是CPU層面的。

          正常情況下,singleton = new Singleton()的步驟是按照1->2->3這種步驟進(jìn)行的,但是一旦JVM做了指令重排序,那么順序很可能編程1->3->2,如果是這種順序,可以發(fā)現(xiàn),在3步驟執(zhí)行完singleton對(duì)象就不等于null,但是它其實(shí)還沒做步驟二的初始化工作,但是另一個(gè)線程進(jìn)來(lái)時(shí)發(fā)現(xiàn),singleton不等于null了,就這樣把半成品的實(shí)例返回去,調(diào)用是會(huì)報(bào)錯(cuò)的。

          可以畫個(gè)出現(xiàn)指令重排序的圖加深下理解:

          出現(xiàn)了指令重排序后,按照上圖的流程邏輯,很可能會(huì)返回還沒完成初始化的singleton對(duì)象,導(dǎo)致使用這個(gè)對(duì)象時(shí)報(bào)錯(cuò),而volatile關(guān)鍵字的作用之一就是禁止指令重排序。

          總結(jié)

          DCL使用volatile關(guān)鍵字,是為了禁止指令重排序,避免返回還沒完成初始化的singleton對(duì)象,導(dǎo)致調(diào)用報(bào)錯(cuò),也保證了線程的安全。


          瀏覽 57
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  日本黄色电影网 | 99国精产品自偷自偷综合 | 国产婬荡交换XXXⅩ | 午夜久久精品嫖妓av一区二区三区 | 高清无码在线看 |