1. 程式人生 > >java double-check-lock併發問題

java double-check-lock併發問題


// thread 1
class Singleton {
    private static Singleton INSTANCE

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton() // new物件
                }
            }
        }
        return
INSTANCE; } } // thread 2 Singleton.getInstance(); // 這裡可能獲取到未建立完整的Singleton物件引用

這個程式碼是無法工作的. 因為這個可能讓其他執行緒看到沒有完全構件好的物件:

JVM建立一個物件的過程來看,分為:“裝載”,“連線”,“初始化”三個步驟。
在連線步驟中包含“驗證”,“準備”,“解析”這幾個環節。
為一個物件分配記憶體的過程是在連線步驟的準備環節,它是先於“初始化”步驟的,而建構函式的執行是在“初始化”步驟中的。

簡單理解,上述INSTANCE = new Singleton()的過程是非原子操作,其實際過程如下:

// 實際上的步驟:
1.allocateMemory -> object 
2.Singleton._INSTANCE = object
3.init object attributes

假如兩個執行緒同時搶奪執行上述程式碼,此時因為執行緒1和執行緒2沒有用同步,他們之間不存在“Happens-Before”規則的約束,所以線上程1建立Singleton物件的 1,2這兩個步驟對於執行緒2來說會有可能出現1可見,2不可見
造成了執行緒2獲取到了一個未建立完整的Singleton物件引用

//其中分析一個物件建立過程的部分摘抄如下[Symantec(賽門鐵克)JIT]:

singletons[i].reference = new
Singleton(); to the following (note that the Symantec JIT using a handle-based object allocation system). ------- 0206106A mov eax,0F97E78h ; allocate space for 0206106F call 01F6B210 ; Singleton, return result in eax 02061074 mov dword ptr [ebp],eax ; EBP is &singletons[i].reference 02061077 mov ecx,dword ptr [eax] ; store the unconstructed object here. 02061079 mov dword ptr [ecx],100h ; dereference the handle to 0206107F mov dword ptr [ecx+4],200h ; get the raw pointer 02061086 mov dword ptr [ecx+8],400h ; Next 4 lines are 0206108D mov dword ptr [ecx+0Ch],0F84030h ; Singleton's inlined constructor ------- As you can see, the assignment to singletons[i].reference is performed before the constructor for Singleton is called. This is completely legal under the existing Java memory model, and also legal in C and C++ (since neither of them have a memory model).

jdk5以上版本可使用volatitle修復,jdk5以下版本基本無法修復

/*
* @since 1.5
*/
class Singleton {
    private static volatile Singleton INSTANCE

    public static Singleton getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton.class) {
                if (INSTANCE == null) {
                    INSTANCE = new Singleton() // new物件
                }
            }
        }
        return INSTANCE;
    }

}
附: Doug Lea
public class DoubleCheckTest
  {


  // static data to aid in creating N singletons
  static final Object dummyObject = new Object(); // for reference init
  static final int A_VALUE = 256; // value to initialize 'a' to
  static final int B_VALUE = 512; // value to initialize 'b' to
  static final int C_VALUE = 1024;
  static ObjectHolder[] singletons;  // array of static references
  static Thread[] threads; // array of racing threads
  static int threadCount; // number of threads to create
  static int singletonCount; // number of singletons to create


  static volatile int recentSingleton;


  // I am going to set a couple of threads racing,
  // trying to create N singletons. Basically the
  // race is to initialize a single array of 
  // singleton references. The threads will use
  // double checked locking to control who 
  // initializes what. Any thread that does not
  // initialize a particular singleton will check 
  // to see if it sees a partially initialized view.
  // To keep from getting accidental synchronization,
  // each singleton is stored in an ObjectHolder 
  // and the ObjectHolder is used for 
  // synchronization. In the end the structure
  // is not exactly a singleton, but should be a
  // close enough approximation.
  // 


  // This class contains data and simulates a 
  // singleton. The static reference is stored in
  // a static array in DoubleCheckFail.
  static class Singleton
    {
    public int a;
    public int b;
    public int c;
    public Object dummy;

    public Singleton()
      {
      a = A_VALUE;
      b = B_VALUE;
      c = C_VALUE;
      dummy = dummyObject;
      }
    }

  static void checkSingleton(Singleton s, int index)
    {
    int s_a = s.a;
    int s_b = s.b;
    int s_c = s.c;
    Object s_d = s.dummy;
    if(s_a != A_VALUE)
      System.out.println("[" + index + "] Singleton.a not initialized " +
s_a);
    if(s_b != B_VALUE)
      System.out.println("[" + index 
                         + "] Singleton.b not intialized " + s_b);

    if(s_c != C_VALUE)
      System.out.println("[" + index 
                         + "] Singleton.c not intialized " + s_c);

    if(s_d != dummyObject)
      if(s_d == null)
        System.out.println("[" + index 
                           + "] Singleton.dummy not initialized," 
                           + " value is null");
      else
        System.out.println("[" + index 
                           + "] Singleton.dummy not initialized," 
                           + " value is garbage");
    }

  // Holder used for synchronization of 
  // singleton initialization. 
  static class ObjectHolder
    {
    public Singleton reference;
    }

  static class TestThread implements Runnable
    {
    public void run()
      {
      for(int i = 0; i < singletonCount; ++i)
        {
    ObjectHolder o = singletons[i];
        if(o.reference == null)
          {
          synchronized(o)
            {
            if (o.reference == null) {
              o.reference = new Singleton();
          recentSingleton = i;
          }
            // shouldn't have to check singelton here
            // mutex should provide consistent view
            }
          }
        else {
          checkSingleton(o.reference, i);
      int j = recentSingleton-1;
      if (j > i) i = j;
      }
        } 
      }
    }

  public static void main(String[] args)
    {
    if( args.length != 2 )
      {
      System.err.println("usage: java DoubleCheckFail" +
                         " <numThreads> <numSingletons>");
      }
    // read values from args
    threadCount = Integer.parseInt(args[0]);
    singletonCount = Integer.parseInt(args[1]);

    // create arrays
    threads = new Thread[threadCount];
    singletons = new ObjectHolder[singletonCount];

    // fill singleton array
    for(int i = 0; i < singletonCount; ++i)
      singletons[i] = new ObjectHolder();

    // fill thread array
    for(int i = 0; i < threadCount; ++i)
      threads[i] = new Thread( new TestThread() );

    // start threads
    for(int i = 0; i < threadCount; ++i)
      threads[i].start();

    // wait for threads to finish
    for(int i = 0; i < threadCount; ++i)
      {
      try
        {
        System.out.println("waiting to join " + i);
        threads[i].join();
        }
      catch(InterruptedException ex)
        {
        System.out.println("interrupted");
        }
      }
    System.out.println("done");
    }
  }

相關推薦

java double-check-lock併發問題

// thread 1 class Singleton { private static Singleton INSTANCE public static Singleton getInstance() { if (INSTA

java double check lock

對於多執行緒程式設計來說,同步問題是我們需要考慮的最多的問題,同步的鎖什麼時候加,加在哪裡都需要考慮,當然在不影響功能的情況下,同步越少越好,鎖加的越遲越優是我們都必須認同的。DCL(Double Check Lock)就是為了達到這個目的。  DCL簡單來說就是check

關於java中的double check lock

實現一個正確的單例模式 在熟悉的單例模式中你或許會遇到下面的方式來實現一個單例: // version 1 class Singleton { private static Singleton _INSTANCE static Singl

java double-check lazy load------effedctive java 第七十一條:慎用延遲初始化(這個變數的作用是確保field只在已經被初始化的情況下讀取一次)

private volatile FieldType field; FieldType getField(){ FieldType result = field; if(result==null){ synchronized(this){ re

單例模式中的Double check lock

Double check lock package test; public class Singleton { public static Singleton singleton; private Singleton(){ }

沒理解double check lock的問題

嗯,看了些連結 還是沒想明白就算第一個執行緒中建立物件與給引用賦值的順序反了,難道syschronized關鍵字不能保證這兩個操作都執行完之後在刷到主記憶體嗎? 在第一個執行緒執行過程中,第二個執行緒讀取主記憶體中的引用,因該還是為null的阿 之後再去

Java 單例模式中使用雙重檢查(Double-Check

在 Effecitve Java 一書的第 48 條中提到了雙重檢查模式,並指出這種模式在 Java 中通常並不適用。該模式的結構如下所示: public Resource getResource() {     if (resource == null)

Java使用double check(雙重檢查)實現單例模式的一個小細節

public class Singleton { private static Singleton singleton; private Singleton() { } public static Singleton getIn

Double-check idiom for lazy initialization of instance fields

nbsp ges field initial 技術分享 img image check fields Double-check idiom for lazy initialization of instance fields

zbb20180913 java thread JDK-Lock

val nal 關鍵字 cell sign wait 接口 jdk 線程安全 JDK1.5-Lock 在 jdk1.5 之後,並發包中新增了 Lock 接口(以及相關實現類)用來實現鎖功能,Lock 接口提供了與 synchronized 關鍵字類似的同步功能,但需要在使用

java double 轉換成 #.00 格式String 防止科學計數法

public static String double2String(Double d){ return d==null? "0.00" : String.format("%.2f", d); } 在報文前邊加8位長度 public String dea

JAVA學習筆記(併發程式設計 - 玖)- 多執行緒併發拓展

文章目錄 死鎖 概念 產生條件 例子 併發最佳實踐 Spring與執行緒安全 死鎖 概念 死鎖是指兩個或兩個以上的程序在執行過程中,由於競爭資源或者由於彼此通訊而造成的一種阻塞的現象

JAVA學習筆記(併發程式設計 - 捌)- 執行緒池

文章目錄 執行緒池 執行緒池的好處 執行緒池原理 執行緒池狀態 執行緒池常用方法 使用ThreadPoolExecutor建立執行緒池 執行緒池 執行緒資源必須通過執行緒池提供,不允許在應用中

JAVA學習筆記(併發程式設計 - 柒)- J.U.C元件2

J.U.C-FutureTask 在Java中一般通過繼承Thread類或者實現Runnable介面這兩種方式來建立執行緒,但是這兩種方式都有個缺陷,就是不能在執行完成後獲取執行的結果,因此Java 1.5之後提供了Callable和Future介面,通過它們就可以在任務執行完畢之後得到

JAVA學習筆記(併發程式設計 - 陸)- J.U.C之AQS及其相關元件詳解

文章目錄 J.U.C之AQS-介紹 關於AQS裡的state狀態: 關於自定義資源共享方式: 關於同步器設計: 如何使用: 具體實現的思路: 設計思想: 基於AQS的同步元件: AQS小結:

2018最新技術JAVA架構師高併發分散式微服務架構網際網路電商dubbo

借用 Java 併發程式設計實踐中的話:編寫正確的程式並不容易,而編寫正常的併發程式就更難了。相比於順序執行的情況,多執行緒的執行緒安全問題是微妙而且出乎意料的,因為在沒有進行適當同步的情況下多執行緒中各個操作的順序是不可預期的。 併發程式設計相比 Java 中其他知識點學習起來門檻相對較高,學習起來比較費

Java Reactive 非同步與併發程式設計

Java Reactive 非同步與併發程式設計 【摘要】Reactive 程式設計在多數人眼中是非同步、併發的“銀彈/神器”。本文分析了Reactive 執行原理,說明 Reactive 程式設計是資料驅動的,而不是“事件”驅動的。Reactive 程式設計分為資料來源準備、資料流建模、排程者分

java 併發包 Lock Condition實現阻塞佇列

import java.util.LinkedList; import java.util.List; import java.util.Random; import java.util.concurrent.locks.Condition; import java.util.concurrent.

Java處理網際網路高併發(專題精講)

引子: 高併發是網際網路應用的一大特點,也是網際網路應用不可避免的一個問題;比如 淘寶雙11購物狂歡節,京東618購物促銷節,12306春節火車票,促銷,秒殺等; 解決高併發問題是一個系統工程,需要站在全域性高度統籌謀劃,從多個角度進行架構設計,在實踐中,我們探索、總結和提煉出來了很多應

JAVA——Double.valueOf 與 parseDouble 的區別

Double.valueOf 的返回值型別為Double。 Double.parseDouble的返回值型別為double。 但是由於後期JAVA的自動的封裝和自動的解裝,使得這兩個函式區分並不明顯。 package a; import java.util.HashSet; impo