线程安全类中出现意外行为

时间:2016-01-20 10:26:47

标签: java multithreading thread-safety

我尝试创建一个线程安全类,允许跟踪某些内容的扫描。我的班级是:

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;

import org.junit.Test;

public class ScanInProgressTest {

    @Test
    public void testConcurrency() throws Exception {

        // given
        Integer scanId = 1;
        int nbScans = 500_000;
        ScanInProgress scanInProgress = new ScanInProgress(scanId, nbScans);

        // when
        for (int i = 1; i <= nbScans / 2; i++) {
            new AddError(scanInProgress).start();
            new AddSuccess(scanInProgress).start();
        }

        Thread.sleep(1000);

        // then
        assertEquals(nbScans, scanInProgress.getNbResponses());
        assertEquals(nbScans / 2, scanInProgress.getNbSuccesses());

    }

    private class AddError extends Thread {

        private ScanInProgress scanInProgress;

        public AddError(ScanInProgress scanInProgress) {
            this.scanInProgress = scanInProgress;
        }

        @Override
        public void run() {
            int before = scanInProgress.getNbResponses();
            scanInProgress.addError();
            int after = scanInProgress.getNbResponses();
            assertTrue("Add error: before=" + before + ", after=" + after, before < after);
        }

    }

    private class AddSuccess extends Thread {

        private ScanInProgress scanInProgress;

        public AddSuccess(ScanInProgress scanInProgress) {
            this.scanInProgress = scanInProgress;
        }

        @Override
        public void run() {
            int beforeResponses = scanInProgress.getNbResponses();
            int beforeSuccesses = scanInProgress.getNbSuccesses();
            scanInProgress.addSuccess();
            int afterResponses = scanInProgress.getNbResponses();
            int afterSuccesses = scanInProgress.getNbSuccesses();
            assertTrue("Add success responses: before=" + beforeResponses + ", after=" + afterResponses, beforeResponses < afterResponses);
            assertTrue("Add success successes: before=" + beforeSuccesses + ", after=" + afterSuccesses, beforeSuccesses < afterSuccesses);
        }

    }

}

我有以下单元测试类:

Exception in thread "Thread-14723" java.lang.AssertionError: Add success successes: before=7362, after=7362
    at org.junit.Assert.fail(Assert.java:88)
    at org.junit.Assert.assertTrue(Assert.java:41)

当我运行测试时,我可以在日志中定期看到此错误:

scanInProgress.addSuccess()

断言让我想到当我调用方法scanInProgress.getNbSuccesses()然后调用nbResponses.incrementAndGet()时,第一个方法nbResponses.get()中的指令尚未被确认,而第二个方法中的指令{{1返回一些东西。

我该怎么做才能纠正这个问题?

1 个答案:

答案 0 :(得分:0)

据我解读这个问题,我认为你需要使用锁。在获取或设置变量之前,您需要获取锁定。这样,不能同时设置和读取变量。你得到类似下面的代码。

public final static Object LOCK = new Object();

private int yourvariable;

public void setVar(int var){
    synchronized(LOCK){
        yourvariable = var;
    }
}

public int getVar(){
    int toReturn;
    synchronized(LOCK){
        toReturn = yourvariable;
    }
    return toReturn;
}

注意:如果您的ScanInProgessClass是唯一的ScanInProgressClass类,则可以使用this而不是LOCK对象。