2011-03-19 60 views
0

我有一個程序使用單例模式。我需要使用線程,記住使用線程機械化之前和之後的輸出應該是相同的。我的意思是避免線程忽略單例並創建更多一個對象的「斷線模式」。但是,我失敗了。我試圖使用「同步」,但沒有改變。同樣的錯誤結果。java編程和java單線程多線程問題(單線程與多線程)

我主要用了Runnable

public class Main implements Runnable { 
    Main(){} 
    public void run() 
     { 
      Counter[] counters = new Counter[5]; 
      for(int i = 0; i < counters.length; i++) 
       { 
        counters[i] = Counter.getCounter(); 
       } 
      for(int i = 0; i < counters.length; i++) 
       { 
        counters[i].increment(); 
        System.out.println("counter[" + i + "] = " + counters[i]); 
       } 

      for(int i = 0; i < 5; i++) { 

       counters[i].decrement(); 
       System.out.println("counter[" + i + "] = " + counters[i]); 
       }} 

    public static void main(String[] args) 
    { 

     Main m1=new Main(); 
     Main m2=new Main(); 
     Main m3=new Main(); 
     new Thread(m1).start(); 
     new Thread(m2).start(); 
     new Thread(m3).start(); 
    } 
} 

它適用Singleton模式

public class Counter { 

     private static Counter myInstance = null; 


     public static Counter getCounter() 
     { 
      if(myInstance == null) 
       { 
        synchronized (Counter.class)     { 
         if(myInstance == null) 
         { 
          myInstance = new Counter(); 
         } 
        } 
       } 




     return(myInstance); 


     } 

     private int myCounter; 

     private Counter() { 
     myCounter = 0; 
     } 

     public void increment() { 
     myCounter++; 
     } 

     public void decrement() { 
     myCounter--; 
     } 

     public String toString() { 
     return(Integer.toString(myCounter)); 
     } 
    } 
+0

'java.util'應該有一個懶惰的評價'Lazy'類;這經常是需要的,而人們不知道如何正確地做。 「內部類單身人士」和「枚舉單身人士」是黑客,只爲全球狀態工作。 Spring只是讓你在xml中編寫相同的東西。出於某種原因,人們認爲xml不是代碼。對他們的老闆很好。 – irreputable 2011-03-20 03:24:00

回答

3

雙重檢查鎖定模式用於在Java中被打破的其他類。我不知道新內存模型是否修復它。

除了「我真的需要一個單身人士嗎?」這個問題之外,單身人士購買你的懶惰實例化到底是什麼?

Nothing

如果你的單例實例化的代價很高,並且有可能你不使用它,這可能是合理的。但這裏沒有一個是這種情況。

所以,如果你一定要,寫這樣的:

public class Counter 
{ 
    // Edited at the recommendation of Sean and "Effective Java"; see below 
    private static class InstanceHolder 
    { 
     private static final Counter INSTANCE = new Counter(); 
    } 

    private Counter() {} 

    public static Counter getInstance() { return InstanceHolder.INSTANCE; } 

    // The rest of the implementation follows. 
} 
+0

你可能想要在聲明時初始化'instance'變量。 – CarlosZ 2011-03-19 21:37:47

+0

該死的藥物.....感謝指出。 – duffymo 2011-03-19 21:46:09

+0

其值得注意的是,類延遲加載,所以只使用'公共靜態final'值或'enum'用一個數值很可能是一樣的好,更簡單/安全。 – 2011-03-19 21:58:46

6

不要使用雙重檢查單例模式,它不是做現代的方式,無論是損壞或沒有。

單身在Java中應該使用任一內部類或枚舉來實現(見Effective Java項目3:強制singleton屬性與私有構造函數或枚舉類型):

一)內部類的Singleton圖案:

public class MySingleton{ 
    private MySingleton(){} 
    private static class InstanceHolder{ 
     private static final MySingleton INSTANCE = new MySingleton(); 
    } 
    public static MySingleton getInstance(){ 
     return InstanceHolder.INSTANCE; 
    } 

} 

b)中的Enum Singleton模式

public enum MySingleton{ 
    INSTANCE; 

    public static MySingleton getInstance(){ 
     return INSTANCE; 
    } 

} 
+1

有效的Java是在這裏提到的正確的書(+1)。 – atamanroman 2011-03-19 21:39:37

+0

鏈接到枚舉不再工作 – 2016-12-29 12:40:40

+0

@ BenSchmidt謝謝。維基百科的文章被改變,不幸的是,刪除鏈接 – 2016-12-29 13:37:43

0

您必須同步整個方法。 Here's why your code is wrong

最有意義的是在靜態塊中初始化singleton並且不檢查任何東西,除非你有成千上萬個通常不用的singleton類,它不會影響性能。

+0

「...成千上萬....單身......」 - ? – duffymo 2011-03-19 21:30:49

+0

@duffymo:他的意思是成千上萬的不同類別,其是單身,這的確是可能的 – atamanroman 2011-03-19 21:41:34

+0

是的,我知道他的意思的。可能?是。可取?我對此表示懷疑。 – duffymo 2011-03-19 22:44:42

1

不完全確定你要達到的目標,但有一些錯誤。

  • increment/decrement不是線程安全的。 ++和 - 不是原子操作。您需要同步這兩種方法或使用AtomicInteger及其原子增量/減量方法

  • 您正在讀取與修改相反的計數器的值,因此另一個線程可能已更改爲更新和讀取之間的值它爲println。我會建議使用的AtomicInteger和遞增/遞減返回新值,如果你需要顯示它

  • 綜觀以上兩點你也許可以用一個靜態的AtomicInteger實例

  • 雙重檢查更換計數器鎖定在getCounter可能被破壞。我不確定靜態的行爲在同步的可見性範圍之外。爲了安全起見我會刪除最初的零檢查

  • 你需要的輸出是不會出來的驗證碼。每個線程(其中有3個線程)採用相同的計數器實例,5次,每次都打印兩次。我的數字是30個輸出語句。

  • 這些輸出的順序永遠無法預測,因爲線程正在競爭增加/減少(單個)計數器值,因此它可能會以隨機方式上下顛簸。加上數值的打印可能出現無序的線程競爭太

  • 這是不好的做法,對一類同步,尤其是公共類和其他代碼可以sychronize它與你的鎖干擾。最好的辦法就是對那個

  • 一個private static final Object lock = new Object();和同步你並不需要把()圍繞return語句

希望一些幫助!多線程代碼是一項困難/危險的業務,所以請謹慎行事!也許如果你問了一個問題,說明你的目標,某人可以用一些提示和/或模型答案來幫助你。

+0

+1他不僅僅有一個問題 – irreputable 2011-03-20 03:16:31