2014-08-29 67 views
1

我正在努力圍繞我的課程做一個線程安全的實現,以便在Servlet或代碼中的多個「客戶端」之間共享我的類。線程安全地更新共享對象而不會影響性能?

假裝我有以下MySingleton類,這是一個用Configuration對象初始化的單例。但是,可以觀察到Configuration對象,因此如果發生更改,單身人士將訂閱通知。重要的關鍵點:

  • 配置可隨時更改(無法預測)

  • 當配置解析,它的值保存到MySingleton

    的成員字段
  • 單元素的公共方法使用這些字段生成返回結果

請參閱下面的簡化代碼:

public class MySingleton 
    implements IConfigurationObserver { 
    // Member(s) 
    private static volatile MySingleton instance; 
    private final Configuration configuration; 
    private String firstParam; 
    private String secondParam; 

    // Constructor(s) 
    private MySingleton(Configuration configuration) { 
     this.configuration = configuration; 
     parseConfiguration(); 
     configuration.addObserver(this); 
    } 

    public static MySingleton getInstance(Configuration configuration) { 
     // Perform synchronized creation if applicable (double locking technique) 
     MySingleton syncInstance = instance; 
     if (syncInstance == null) { 
      synchronized(MySingleton.class) { 
       syncInstance = instance; // Verify once again after synchronizing 
       if(syncInstance == null) { 
        syncInstance = new MySingleton(configuration); 
        instance = syncInstance; 
       } 
      } 
     } 

     return syncInstance; 
    } 

    // Private Method(s) 
    private void parseConfiguration() { 
     // Import values from the configuration 
     this.firstParam = configuration.get(0); 
     this.secondParam = configuration.get(1); 
    } 

    // Public Method(s) 
    public String buildSentence() { 
     // Build a new string based on values pulled from the configuration 
     StringBuilder strBuilder = new StringBuilder(); 
     strBuilder.append(firstParam); 
     strBuilder.append(" - "); 
     strBuilder.append(secondParam); 

     return strBuilder.toString(); 
    } 

    // Observer Method(s) 
    @Override 
    public void onConfigurationUpdated() { 
     // The configuration has changed. Parse it again. 
     parseConfiguration(); 
    } 
} 

類適用於自己的罰款(單線程環境),但這裏的一些威脅,我想在多線程方案,以消除:

  • 如果兩個更新到Configuration做在很短的時間內,有可能在第二次通話開始之前第一次撥打parseConfiguration()尚未完成。這個很容易解決,我可以讓parseConfiguration()同步(對吧?)。但是...

  • 當配置通知我們的單身人士時,假裝我們正在呼叫buildSentence()。我不希望buildSentence()對firstParam和secondParam使用舊值的混合(即,如果parseConfiguration()已完成)。因此,我可以爲parseConfiguration()buildSentence()Configuration對象上放置一個同步塊,但後來我遇到了嚴重的性能問題:我不能有多個併發呼叫到buildSentence()。說實話,對我來說,最理想的情況是:

    • 如果buildSentence()運行和Configuration更新時,parseConfiguration()要等到buildSentence()是在運行

    • 之前如果parseConfiguration()正在運行,調用到buildSentence()必須等到parseConfiguration()結束開始

    • 然而之前,一旦parseConfiguration()結束後,我想允許多個線程運行buildSentence()在同一時間。鎖定只能在更新即將發生或發生時纔會發生。

我如何重構MySingleton允許我上面列出的理想的「規則」?可能嗎?


我一直在想一個涉及信號量的解決方案。即:執行buildSentence()時,檢查信號量是否可用。如果是的話,繼續(不阻擋)。如果否,請等待。並且parseConfiguration會在執行期間鎖定信號量。但是,如果有人有直接的建議方法,我不想過度設計這個問題。讓我知道!

+0

a good [resource](http://stackoverflow.com/questions/70689/what-is-an-efficient-way-to-implement-a-singleton-pattern-in-java/71399#71399)for java中的單身人士。 – 2014-08-29 06:33:42

+0

僅供參考 - 我不認爲這個班級是單身人士的事實會對這個問題的正確答案產生任何影響。 – 2014-08-29 06:37:24

+0

@Duncan你是對的。我可能會重新提出這個問題,使其更一般化。 – hbCyber 2014-08-29 06:39:43

回答

3

我想我會去這樣的事情:

public class MySingleton 
    implements IConfigurationObserver { 
    // Member(s) 
    private static volatile MySingleton instance; 
    private final Configuration configuration; 
    private volatile ParsedConfiguration currentConfig; 

    // Constructor(s) 
    private MySingleton(Configuration configuration) { 
     this.configuration = configuration; 
     parseConfiguration(); 
     configuration.addObserver(this); 
    } 

    public static MySingleton getInstance(Configuration configuration) { 
     // Perform synchronized creation if applicable (double locking technique) 
     MySingleton syncInstance = instance; 
     if (syncInstance == null) { 
      synchronized(MySingleton.class) { 
       syncInstance = instance; // Verify once again after synchronizing 
       if(syncInstance == null) { 
        syncInstance = new MySingleton(configuration); 
        instance = syncInstance; 
       } 
      } 
     } 

     return syncInstance; 
    } 

    // Private Method(s) 
    private void parseConfiguration() { 
     // Import values from the configuration 
     currentConfig = configuration.getNewParsedConfiguration(); 
    } 

    // Public Method(s) 
    public String buildSentence() { 
     // Build a new string based on values pulled from the current configuration 
     ParsedConfiguration configInUse = currentConfig; 
     StringBuilder strBuilder = new StringBuilder(); 
     strBuilder.append(configInUse.getFirstParam()); 
     strBuilder.append(" - "); 
     strBuilder.append(configInUse.getSecondParam()); 

     return strBuilder.toString(); 
    } 

    // Observer Method(s) 
    @Override 
    public void onConfigurationUpdated() { 
     // The configuration has changed. Parse it again. 
     parseConfiguration(); 
    } 
} 

拿筆記保障的移動currentConfig一個局部變量在buildSentence年初所以參考使用ParsedConfig不能改變中期-方法。

+0

我不知道'getNewParsedConfiguration()'需要'同步' - 你能解釋爲什麼嗎?注意OP:'ParsedConfiguration'確實需要不可變(不僅是有效的不可變的)才能工作。 – 2014-08-29 07:11:47

+1

@Duncan它不僅不需要*被同步,而且如果是的話它不會幫助線程安全。相反,'currentConfig'需要是'volatile'。 – 2014-08-29 07:16:42

+0

@MarkoTopolnik同意,沒有'volatile',一些線程可能會看到'currentConfig'的舊值。 – 2014-08-29 07:20:12

相關問題