2012-02-22 21 views
2

更新:這是一個靜態的深埋在一些代碼中,它僅用於幾個指令。謝謝大家的建議。Java HashMap在某些線程中有時會返回錯誤的值

我們沒有在線程之間使用一個HashMap(是的,這很糟糕,原因很多)。每個線程都有自己的HashMap。

我們有一個擴展自Thread的類。在Thread.run()中,我們創建一個HashMap,在其中設置一個鍵/值對,並將該HashMap傳遞給一個方法。該方法從HashMap中檢索值,將其插入到字符串中並返回字符串。

有時返回的字符串有不同的值(仍在Thread.run()中)。這隻發生在具有3個以上物理內核的硬件上。而且它只發生過兩次(在我們添加日誌記錄以幫助我們確切地發現正在發生的事情之前)。

任何想法爲什麼會發生這種情況。

更新:這裏是完整的代碼。 ProcessTxt是從HashMap中提取值並將其放入字符串中。

import java.io.*; 
import java.util.HashMap; 

import junit.framework.TestCase; 
import net.windward.datasource.dom4j.Dom4jDataSource; 
import net.windward.xmlreport.ProcessReport; 
import net.windward.xmlreport.ProcessTxt; 

/** 
* Test calling from multiple threads 
*/ 
public class TestThreads extends TestCase { 

    private static String path = "."; 

    // JUnit stuff 
    public TestThreads(String name) { 
     super(name); 
    } 

    // Get logging going - called before any tests run 
    protected void setUp() throws Exception { 
     ProcessReport.init(); 
    } 

    // this is not necessary - called after any tests are run 
    protected void tearDown() { 
    } 

    private static final int NUM_THREADS = 100; 

    private boolean hadWithVarError = false; 


    /** 
    * Test that each thread has unique variables. 
    */ 
    public void testRunReportsWithVariables() throws Exception { 

     // run 10 threads 
     ReportThreadWithVariables[] th = new ReportThreadWithVariables[NUM_THREADS]; 
     for (int ind = 0; ind < NUM_THREADS; ind++) { 
      th[ind] = new ReportThreadWithVariables(this, ind); 
      th[ind].setName("Run " + ind); 
     } 
     for (int ind = 0; ind < NUM_THREADS; ind++) 
      th[ind].start(); 

     boolean allDone = false; 
     while (!allDone) { 
      Thread.sleep(100); 
      allDone = true; 
      for (int ind = 0; ind < NUM_THREADS; ind++) 
       if (th[ind].isAlive()) 
        allDone = false; 
     } 

     assertTrue(!hadWithVarError); 
    } 

    public static class ReportThreadWithVariables extends Thread { 

     private TestThreads obj; 
     private int num; 

     public ReportThreadWithVariables(TestThreads tt, int num) { 
      obj = tt; 
      this.num = num; 
     } 

     public void run() { 

      try{ 
       System.out.println("starting " + num); 
       ByteArrayOutputStream out = new ByteArrayOutputStream(); 
       ProcessTxt pt = new ProcessTxt(new FileInputStream(new File(path, "Thread_Test.docx")), out); 

       pt.processSetup(); 

       // don't use order1.xml, but need a datasource. 
       Dom4jDataSource datasource = new Dom4jDataSource(new FileInputStream(new File(path, "order1.xml"))); 
       HashMap map = new HashMap(); 
       map.put("num", new Integer(num)); 
       datasource.setMap(map); 
       pt.processData(datasource, ""); 

       pt.processComplete(); 
       String result = out.toString().trim(); 
       System.out.println("complete " + num + ", result = " + result); 

       String expected = "Number: " + num; 
       if (!result.equals(expected)) 
        obj.hadWithVarError = true; 
       assertEquals(expected, result); 
      } catch (Throwable e) { 
       obj.hadWithVarError = true; 
       e.printStackTrace(); 
      } 

     } 
    } 
} 

(編輯格式代碼)

+10

關鍵是什麼?一個簡短但完整的程序展示問題將*真正*幫助... – 2012-02-22 22:23:52

+0

什麼類型的價值可能也是相關的。這些值是否可能包含一個被引用並在其他地方修改過的對象? – VeeArr 2012-02-22 22:36:40

+0

請添加一些示例代碼。 – 2012-02-22 22:44:40

回答

1

由於缺乏代碼和完全基於已被寫入我要假設的東西是static。也就是說,靜態成員正在被存儲到/寫入的地方。

+0

這是我們首先尋找的東西 - 沒有什麼是靜態的。此外,這很少發生 - 我們必須在4核心系統上運行約4分鐘才能獲得此功能。而在2核心系統上,它從來沒有發生過。我會發布代碼。 – 2012-02-22 22:48:02

0

num是不可變的,其他變量(字符串,映射)是本地的,因此ReportThreadWithVariables看起來是線程安全的。在我看來,問題在於對外部對象的調用,而不是你發佈的內容。 您使用的類是否記錄爲線程安全?

對於exampel,javadoc of the processData method指出,它不應該被多次調用相同的數據源,你似乎正在做(相同的文件名)。

ps :(無關)你可以使用CountDownLatch而不是while循環。

相關問題