2008-10-12 63 views
1

基本上,我有一個包含2個方法的類:一個用於將對象序列化爲XML文件,另一個用於從XML讀取對象。 下面是從恢復的對象的方法的同步部分的一個示例:同步java中的字符串方法參數的io操作?

public T restore(String from) throws Exception { 
    // variables declaration 
     synchronized (from) { 
      try { 
       decoder = new XMLDecoder(new BufferedInputStream(
         new FileInputStream(from))); 
       restoredItem = decoder.readObject(); 
       decoder.close(); 
      } catch (Exception e) { 
       logger.warning("file not found or smth: " + from); 
       throw new Exception(e); 
      } 
     } 
    // try to cast it 
    } 

類似的方法序列化的對象時被取。現在,當我創建一個單元測試時,每個線程創建10個線程,試圖序列化並立即讀取一個布爾值或一個字符串,它將失敗,顯示出現ClassCastException。這讓我覺得我的序列化錯了(在單線程環境中一切正常)。如果你一直陪着我到這一點:),這裏有兩個問題,我需要你的幫助:

  1. 是否有意義的同步傳遞給方法的字符串參數(考慮到有一個字符串池在java中)?順便說一句,我試着同步XMLSerializer類本身,結果保持不變。
  2. 我該如何正確同步單個文件上的io操作?
+0

哪一行是ClassCastException發生? – 2008-10-12 13:13:50

+0

restoredItem對象是實例變量還是局部變量?這可能是多線程問題的根源。一個解碼器解碼一個對象,下一個線程用其他東西覆蓋它。 – 2008-10-12 17:20:28

回答

5

1. 是的,可以同步字符串,但是您需要在字符串上進行同步。 intern()爲了總是得到相同的對象

StringBuffer sb = new StringBuffer(); sb.append("a").append("b"); 
String a = new String(sb.toString()); 
String b = new String(sb.toString()); 
a == b; //false 
a.equals(b); //true 
a.intern() == b.intern(); //true 

既然你要在同一臺顯示器上同步,你想要的實習生()。

2. 您可能不希望在字符串上進行同步,因爲它可能會在其他位置,代碼內部,第三方或JRE中同步。如果我想保持同步,我會做什麼,是創建一個ID類(它可能只保存字符串),重寫equals()和hashcode()來匹配,將它放在一個WeakHashMap中同時具有鍵和值(同步地圖{syncKey = map.get(新ID(from));如果syncKey == null創建並放入新密鑰} sync {syncKey} )。

3. 然後我再次同步所有,並使用java.util.concurrent.locks.Lock來代替,只是在與上面相同的設置中使用了附加到ID的鎖。

3

鑑於代碼的某些部分丟失,我敢打賭問題在於同步字符串。你不能自由地假設字符串被合併(這會破壞你的同步方案)。

最好的方法是添加一個將關鍵字(字符串)與其實際同步對象相關聯的映射。

除此之外,我會建議玩多線程測試,看看是什麼讓它失敗。例如,如果你讓所有線程只存儲字符串值(而不是字符串或beooleans),測試仍然失敗?

+0

感謝您的回覆。是的,當運行不同的字符串時,測試仍然失敗。我也試着同步他們所在類的整個方法體。結果是一樣的。感謝您在地圖上的提示。 – yanchenko 2008-10-12 13:49:17

2

這種方法存在很多問題。

  1. 除非你調用了String.intern,否則你的字符串可能與你所調用的另一個字符串不同。依靠內部java字符串緩存的行爲不是很健壯。

  2. 你沒有正確處理你的XMLDecoder到finally塊中,在調用期間拋出的任何異常都會泄漏與該FileInputStream相關的文件描述。

  3. 你不需要在其他異常(E)包E,你可以隨便扔é因爲你已經宣佈封閉的方法也拋出異常

  4. 捕捉/拋出異常是一個代碼味道。是的,它是IOException的一個超類,並且可能拋出任何XML解碼異常,但它也是您可能不想捕獲的其他一些事情的超類,例如NullPointerException。

要回答你的問題,你如何序列化訪問共享文件,以確保它不被多個線程使用,是棘手的。 FileChannel.lock()在JVM內部不起作用,它們只是通過機器中的其他進程來修改文件。

我的方法是從這個類中去掉任何鎖,並將其封裝在知道代碼線程問題的東西中。

我也不會傳遞字符串作爲文件名,而是一個文件,它使您能夠使用File.createTempFile(2)在寫入xml和讀取xml的東西之間創建不透明的文件名。

最後,你想同步對共享文件的訪問,或者當你檢測到對同一文件的多個訪問時失敗?

+0

謝謝。修正了我的那些愚蠢的錯誤。 你能詳細解釋一下「File.createTempFile(2)創建不透明的文件名......」嗎?考慮到如果smth已經寫入並不意味着它將在同一會話期間被讀取,否則不要這樣做。 想要同步訪問。 – yanchenko 2008-10-12 14:40:11