2008-09-30 38 views
3

我有一個課程提出翻譯實用程序。翻譯本身應該每30分鐘重新加載一次。我使用Spring Timer支持。基本上,我的課是這樣的:Java 1.4同步:只允許一個方法的實例運行(非阻塞)?

public interface Translator { 
    public void loadTranslations(); 
    public String getTranslation(String key); 
} 

loadTranslations()可以相當長的運行,所以它運行時的舊譯本仍然可用。這是通過在本地地圖中加載翻譯並在加載所有翻譯時更改引用來完成的。

我的問題是:我該如何確保當一個線程已經加載的翻譯,是第二個也嘗試運行,它檢測到並立即返回,而無需啓動第二次更新。

一個synchronized方法只能排隊負載...我仍然在Java 1.4中,所以沒有java.util.concurrent中。

感謝您的幫助!

回答

3

使用某種形式的鎖定裝置,如果它不是已經在進行中僅執行任務。獲取鎖定令牌必須是一個一步的過程。請參閱:

/** 
* @author McDowell 
*/ 
public abstract class NonconcurrentTask implements Runnable { 

    private boolean token = true; 

    private synchronized boolean acquire() { 
     boolean ret = token; 
     token = false; 
     return ret; 
    } 

    private synchronized void release() { 
     token = true; 
    } 

    public final void run() { 
     if (acquire()) { 
      try { 
       doTask(); 
      } finally { 
       release(); 
      } 
     } 
    } 

    protected abstract void doTask(); 

} 

測試代碼,將拋出一個異常,如果任務同時運行:

public class Test { 

    public static void main(String[] args) { 
     final NonconcurrentTask shared = new NonconcurrentTask() { 
      private boolean working = false; 

      protected void doTask() { 
       System.out.println("Working: " 
         + Thread.currentThread().getName()); 
       if (working) { 
        throw new IllegalStateException(); 
       } 
       working = true; 
       try { 
        Thread.sleep(1000); 
       } catch (InterruptedException e) { 
        throw new RuntimeException(e); 
       } 
       if (!working) { 
        throw new IllegalStateException(); 
       } 
       working = false; 
      } 
     }; 

     Runnable taskWrapper = new Runnable() { 
      public void run() { 
       while (true) { 
        try { 
         Thread.sleep(100); 
        } catch (InterruptedException e) { 
         throw new RuntimeException(e); 
        } 
        shared.run(); 
       } 
      } 
     }; 
     for (int i = 0; i < 100; i++) { 
      new Thread(taskWrapper).start(); 
     } 
    } 

} 
0

繼續加載線程的句柄,看看它的運行?

或者你不能只用一個同步標誌來表示某個負載是在進步嗎?

1

我從.NET背景(沒有Java經驗可言),但你可以嘗試一些那種以檢查其是否運行alrady方法的開始簡單的靜態標誌。然後,你需要做的就是確保對該標誌的任何讀/寫都是同步的。所以在開始檢查標誌,如果它沒有設置,設置它,如果設置,返回。如果未設置,則運行該方法的其餘部分,並在完成後取消設置。只要確保將代碼放入try/finally中,並將標記放入終端中,以防出現錯誤時它總是被取消設置。非常簡化,但可能是所有你需要的。

編輯:這實際上可能比同步方法更好。因爲你真的需要一個新的翻譯立即之後才結束?如果需要等待一段時間,您可能不想鎖定太久的線程。

+0

這正好在正確的方向,但同步是不是同步讀取和寫入操作一樣簡單。該領域的檢查和寫作必須是原子... – Guillaume 2010-08-11 21:01:32

0

這實際上等同於需要管理一個Singleton的建設(哇!)做了經典的方式,當代碼:

if (instance == null) { 
    synchronized { 
    if (instance == null) { 
     instance = new SomeClass(); 
    } 
    } 
} 

內部測試是相同的外部測試。外部測試是這樣,我們不經常進入一個同步塊,內部測試是爲了確認自上次做測試以來情況沒有改變(在進入同步之前,線程可能已被搶佔)。

你的情況:

if (translationsNeedLoading()) { 
    synchronized { 
    if (translationsNeedLoading()) { 
     loadTranslations(); 
    } 
    } 
} 

UPDATE:構建一個單身不會將您的JDK1.4下可靠工作的這種方式。爲解釋see here。不過,我認爲在這種情況下你會好的。

+0

這段代碼是不安全的(正如你在你給的鏈接中指出的那樣)。它會排隊呼叫者,這正是我想要阻止的。 – Guillaume 2010-08-11 20:58:29