Java HashMap有时会在某些线程中返回错误的值

时间:2012-02-22 22:22:05

标签: 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();
            }

        }
    }
}

(编辑格式代码)

2 个答案:

答案 0 :(得分:1)

鉴于缺乏代码并仅基于已编写的内容,我将假设某些内容为static。也就是说,沿着该行的某处,静态成员被存储到/写入。

答案 1 :(得分:0)

num不可变,其他变量(字符串,映射)是本地的,因此ReportThreadWithVariables看起来是线程安全的。在我看来,问题在于调用外部对象而不是您发布的内容。 您使用的类是否记录为线程安全?

例如,javadoc of the processData method表示对于您似乎正在执行的相同数据源(同一文件名),不应多次调用它。

ps :(不相关)你可以使用CountDownLatch而不是while循环。