2011-03-17 49 views
2
class MyClass 
{ 
private static MyClass obj; 

public static MyClass getInstance() 
{ 
    if(obj==null) 
    { 
     obj = new MyClass(); 
    } 
    return obj; 
} 

在上面的Java代碼示例,因爲obj與類中的靜態變量, 會的getInstance還是非線程安全的嗎?由於靜態變量由所有線程共享,所以2個併發線程應使用同一個對象。不是嗎?的java:單身,靜態變量和線程安全

VIPUL沙阿

回答

6

因爲靜態變量是如此廣泛的共享,他們是非常非線程安全的。

請考慮如果兩個線程同時調用getInstance會發生什麼情況。兩個線程都將查看共享靜態obj,兩個線程都會在if檢查中看到obj爲空。這兩個線程都會創建一個新的obj

你可能會想:「嘿,它是線程安全的,因爲obj只有一個值,即使它被初始化多次。」該聲明有幾個問題。在我們之前的例子中,getInstance的調用者都會得到他們自己的obj。如果這兩個呼叫者保持對obj的引用,那麼您將有多個正在使用的單例實例。

即使在我們前面的例子求助者只是做:MyClass.getInstance();並沒有節省什麼返回MyClass.getInstance();一個參考,你仍然可以結束在這些線程獲得不同的情況下,從後面的getInstance。即使調用getInstance不會同時發生,您甚至可以進入創建obj的新實例的情況!

我知道我的最後一個看法似乎與直覺不符,因爲最後一次分配到obj似乎是將來調用MyClass.getInstance()時可能返回的唯一值。但是,需要記住的是,JVM中的每個線程都有其自己的本地主內存緩存。如果兩個線程調用getInstance,則它們的本地緩存可能具有分配給obj的不同值,並且將來從這些線程調用getInstance將返回其緩存中的內容。

確保getInstance線程安全的最簡單方法是使該方法同步。這將確保

  1. 兩個線程在同一時間不能進入的getInstance
  2. 線程試圖使用obj永遠不會從緩存中獲得的obj陳舊值

不要嘗試變得聰明並使用雙重檢查鎖定: http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

2

在這種情況下,即使使用靜態變量,getInstance()也不是線程安全的。只有同步才能保證線程安全。

0

下面的例子顯示了一個奇怪的線程保存修改單噸模式,它也支持泛型。

讓它只是線程保存和同步保存只需要同步塊和瞬態和易失性關鍵字。 注意,有一個雙重檢查,同步塊是在一個if中。這帶來更多的性能,因爲同步代價很高。

當然,對於一個真正的單身人士不使用地圖,我說這是一個修改後的。

public class Edge<T> { 
    @SuppressWarnings({"unchecked"}) 
    private static transient volatile HashMap<Object,HashMap<Object, Edge>> instances = new HashMap<Object, HashMap<Object,Edge>>(); 
    /** 
    * This function is used to get an Edge instance 
    * @param <T> Datatype of the nodes. 
    * @param node1, the source node 
    * @param node2, the destination node 
    * @return the edge of the two nodes. 
    */ 
    @SuppressWarnings({"unchecked"}) 
    public static <T> Edge<T> getInstance(T node1, T node2){ 
     if(!(instances.containsKey(node1) && instances.get(node1).containsKey(node2))){ 
      synchronized (Edge.class) { 
       if(!(instances.containsKey(node1) && instances.get(node1).containsKey(node2))){ 
        Edge<T> edge = new Edge<T>(node1, node2); 
        if(!instances.containsKey(node1)){ 
         instances.put(node1, new HashMap<Object, Edge>()); 
        } 
        instances.get(node1).put(node2, edge); 
       } 
      } 
     } 
     return (Edge<T>)instances.get(node1).get(node2); 
    } 
+0

隨機的例子,雙重檢查鎖定不起作用,如果他同步他的getInstance方法,他不需要volatile關鍵字,transient與線程安全無關。雙重檢查鎖定被破壞:http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html – 2011-03-17 14:54:29

+0

一個ammendmant,當使用volatile(在java 5中)時,雙重檢查鎖定會起作用。所以如果他確實使用了雙重檢查鎖定,那麼他會需要volatile關鍵字。 – 2011-03-17 15:14:09

+0

那麼爲什麼投我一票呢?這個例子就是天才。有點隨機確定,但只是減少到需要的就可以做到這一點。如果你不知道這樣的雙重檢查鎖定工作,沒有理由抱怨。 http://books.google.com/books?id=GGpXN9SMELMC&printsec=frontcover&dq=design+patterns&hl=de&ei=EFGCTbyaIozKswbHyaiCAw&sa=X&oi=book_result&ct=result&resnum=2&ved=0CDMQ6AEwAQ#v=onepage&q&f=false 退房第182頁 – 2011-03-17 18:20:12

0
public class Singleton{ 
    private static transient volatile Singleton instance; 
    public static Singleton getInstance(){ 
     if(instance==null)synchronized(Singleton.class){ 
      if(instance==null){ 
       instance = new Singleton(); 
      } 
     } 
     return instance; 
    } 
    private Singleton(){ 
     /*....*/ 
    } 
} 

頁面182: http://books.google.com/books?id=GGpXN9SMELMC&printsec=frontcover&dq=design+patterns&hl=de&ei=EFGCTbyaIozKswbHyaiCAw&sa=X&oi=book_result&ct=result&resnum=2&ved=0CDMQ6AEwAQ#v=onepage&q&f=false

認爲這可以被標記爲現在回答。

0
class MyClass 
{ 
private static MyClass obj; 

private MyClass(){ 
    // your initialization code 
} 
public static synchronized MyClass getInstance() 
{ 
    if(obj==null) 
    { 
     obj = new MyClass(); 
    } 
    return obj; 
} 

我會同意@Manoj。 我相信上述將是實現單身物體的最佳方法之一。 同步使對象線程安全。 即使它是靜態的:)