java - 为什么java并发测试失败?

标签 java multithreading concurrency

我有一个简单的类:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DummyService {
    private final Logger logger = LoggerFactory.getLogger(getClass());
    private boolean dataIndexing = false;

    public boolean isDataIndexing() {
        logger.info("isDataIndexing: {}", dataIndexing);
        return dataIndexing;
    }

    public void cancelIndexing() {
        logger.info("cancelIndexing: {}", dataIndexing);
        dataIndexing = false;
    }

    public void createIndexCorp() {
        logger.info("createIndexCorp: {}", dataIndexing);
        createIndex();
    }

    public void createIndexEntr() {
        logger.info("createIndexEntr: {}", dataIndexing);
        createIndex();
    }

    private void createIndex() {
        logger.info("createIndex: {}", dataIndexing);
        if(dataIndexing)
            throw new IllegalStateException("Service is busy!");
        dataIndexing = true;
        try {
            while(dataIndexing) {
                Thread.sleep(100);
                logger.debug("I am busy...");
            }
            logger.info("Indexing canceled");
        } catch (InterruptedException e) {
            logger.error("Error during sleeping", e);
        } finally {
            dataIndexing = false;
        }
    }
}

和一个单元测试,我想用它来测试对象的行为:

public class CommonUnitTest
{
    @Test
    public void testCreateIndexWithoutAsync() throws InterruptedException {
        final long sleepMillis = 500;
        final DummyService indexService = new DummyService();
        assertFalse(indexService.isDataIndexing());
        new Thread(() -> {
                    indexService.createIndexCorp();
                }
        ).start();
        Thread.sleep(sleepMillis);
        assertTrue(indexService.isDataIndexing());
        // TaskExecutor should fails here
        new Thread(() -> {
                    indexService.createIndexEntr();
                    logger.error("Exception expected but not occurred");
                }
        ).start();
        assertTrue(indexService.isDataIndexing());
        indexService.cancelIndexing();
        Thread.sleep(sleepMillis);
        assertFalse(indexService.isDataIndexing());
    }
}

对象的行为必须是:如果方法 createIndexCorp 或 createIndexEntr 被一个线程调用,则另一个线程必须通过尝试调用此方法之一来获得异常。但这不会发生!这是日志:

2015-10-15 17:15:06.277  INFO   --- [           main] c.c.o.test.DummyService                  : isDataIndexing: false
2015-10-15 17:15:06.318  INFO   --- [       Thread-0] c.c.o.test.DummyService                  : createIndexCorp: false
2015-10-15 17:15:06.319  INFO   --- [       Thread-0] c.c.o.test.DummyService                  : createIndex: false
2015-10-15 17:15:06.419 DEBUG   --- [       Thread-0] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:06.524 DEBUG   --- [       Thread-0] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:06.624 DEBUG   --- [       Thread-0] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:06.724 DEBUG   --- [       Thread-0] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:06.818  INFO   --- [           main] c.c.o.test.DummyService                  : isDataIndexing: true
2015-10-15 17:15:06.820  INFO   --- [           main] c.c.o.test.DummyService                  : isDataIndexing: true
2015-10-15 17:15:06.820  INFO   --- [       Thread-1] c.c.o.test.DummyService                  : createIndexEntr: true
2015-10-15 17:15:06.820  INFO   --- [           main] c.c.o.test.DummyService                  : cancelIndexing: true
2015-10-15 17:15:06.820  INFO   --- [       Thread-1] c.c.o.test.DummyService                  : createIndex: true
2015-10-15 17:15:06.824 DEBUG   --- [       Thread-0] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:06.921 DEBUG   --- [       Thread-1] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:06.924 DEBUG   --- [       Thread-0] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:07.021 DEBUG   --- [       Thread-1] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:07.024 DEBUG   --- [       Thread-0] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:07.121 DEBUG   --- [       Thread-1] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:07.124 DEBUG   --- [       Thread-0] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:07.221 DEBUG   --- [       Thread-1] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:07.224 DEBUG   --- [       Thread-0] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:07.321 DEBUG   --- [       Thread-1] c.c.o.test.DummyService                  : I am busy...
2015-10-15 17:15:07.321  INFO   --- [           main] c.c.o.test.DummyService                  : isDataIndexing: true

您可以看到第二个线程可以启动进程,但它应该得到异常。测试代码中的最后一个断言也失败了。怎么会这样?我不明白这种行为。我尝试使用 volatile 和 synchronized 关键字,但没有任何帮助。 DummyService 有什么问题?

最佳答案

您有 3 个线程,t0、t1 和 tm(主线程)。 操作顺序是这样的:

tm starts t0
t0 checks dataIndexing flag - false, goes into the loop, sets flag to true
tm sleeps
tm starts t1
tm sets indexing flag to false
t1 checks dataIndexing flag - false, goes into the loop, sets flag to true
t0 continues the loop because it missed that brief period when indexing was cancelled

如果在将索引标志设置为 false 之前在主 tm 中 hibernate ,则 t1 将得到异常。 您需要同步访问多个线程之间共享的变量。 IE。检查标志的状态并更改它需要在持有互斥锁的同时完成。

关于java - 为什么java并发测试失败?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33151051/

相关文章:

c# - ASP.NET Web API C# 并发请求导致数据库重复

java - libgdx ScissorStack 未按预期工作

java - 在Struts2中,如何以HTML格式正确显示文本(从文本区域收集)?

java - 在 Hibernate 3.6.10.Final + c3p0 + struts2 中打开新连接时关闭连接

java - 从数据库获取数据时缓存引用的库

scala - future 和延迟 : How to prioritize the execution of some Futures over the others

走例程:Making concurrent API requests

java - 如何从 netbeans 中的另一个内部框架打开内部框架

java - 使用 Java 应用程序进行文本报告测试

c - 为什么更多的线程需要更多的时间来处理?