最清洁的方法测试Java中方法的线程安全性

发布于 2025-02-04 23:28:52 字数 1341 浏览 2 评论 0原文

我一直在尝试为懒惰的Singleton类编写单元测试,以表明此实现是线程不安全的。以下是其GetInstance方法的代码:

private static DateUtil dateUtilInstance;

public static DateUtil getInstance() {
    if (null == dateUtilInstance) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        dateUtilInstance = new DateUtil();
    }
    return dateUtilInstance;
}

用于测试此方案的线程UN -SAVETY,我尝试了一些方法,例如 -

  1. 实现“可呼叫”类,用于使用线程类的回调来轻松编写断言。我发现这种方法在单位测试中有点不干净,因为它涉及创建2个单独的类以测试线程不安全方案

  2. 我发现这种 在那里回调。

  3. 使用“完整的future”以异步方式运行该方法。这是此方法的单元测试代码:

    @Test
    void shouldVerifyThatClassInstantiationIsThreadUnsafe() {
        var task1 = CompletableFuture.supplyAsync(DateUtil::getInstance);
        var task2 = CompletableFuture.supplyAsync(DateUtil::getInstance);
        var instance1 = task1.join();
        var instance2 = task2.join();

        assertNotEquals(instance1, instance2);
    }

如果我单独运行测试,则可以运行良好,但是当我在课堂级别上使用所有其他测试运行时,测试会失败。我是线程概念的新手,所以我对此有一些疑问:

  1. 使用pountableFuture < / code>正确测试多线程方案的方法,还是我混淆 /混合概念?
  2. 为什么在单独运行时进行单位测试工作,但是在与其他测试进行运行时失败?有人可以帮我了解其背后的理由吗?
  3. 还有其他更好的方法来测试此线程安全方案吗?

PS:我已经在这里浏览了其他类似的问题,这些问题也没有解决我分享和需要清楚的单位测试方法。

I have been trying to write a unit test for lazy-instantiated singleton class to demo that this implementation is thread unsafe. Following is the code for its getInstance method:

private static DateUtil dateUtilInstance;

public static DateUtil getInstance() {
    if (null == dateUtilInstance) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        dateUtilInstance = new DateUtil();
    }
    return dateUtilInstance;
}

For testing this scenario for thread un-safety, I tried few approaches such as -

  1. implementing "callable" class for having a callback for thread classes to easily write assertion. I found this approach a bit unclean for unit test as it involved creating of 2 separate classes to test the thread unsafe scenario

  2. tried implementing "runnable" class as well - but similar take as above along with fact that I could not provide a callback there.

  3. Using "CompletableFuture" to run the method in async way. Here is the unit test code for this approach:

    @Test
    void shouldVerifyThatClassInstantiationIsThreadUnsafe() {
        var task1 = CompletableFuture.supplyAsync(DateUtil::getInstance);
        var task2 = CompletableFuture.supplyAsync(DateUtil::getInstance);
        var instance1 = task1.join();
        var instance2 = task2.join();

        assertNotEquals(instance1, instance2);
    }

The test runs fine if I run it individually, but it fails when I run with all other tests at class level. I am novice to concept of threading, so I have some questions around this:

  1. Is this approach of using completableFuture correct for testing the multi-threaded scenario or am I confusing / mixing the concepts?
  2. Why is above unit test working when ran individually but failing when ran with other tests? Can someone please help me understand the rationale behind it?
  3. Any other better approach to test this thread-safety scenario?

PS: I have already browsed through other similar questions here that were coming in suggestions, but they were not addressing the approach for unit test that I have shared and needed clarity on.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

羞稚 2025-02-11 23:28:52

也许是因为ploteablefuture.supplyasync()可以将任务提交到一个仅有一个可用线程的ThreadPool。因此,任务2仅在Task1完成后才执行。

当您使用其他测试进行单元测试时,这可能会发生。不知道更多,无法分辨。

但是,您可以尝试使用足够的线程创建自己的ThreadPool,以并行执行所有内容。尝试以下操作:

    @Test
    void shouldVerifyThatClassInstantiationIsThreadUnsafe() {
    ExecutorService myThreadPool = Executors.newFixedThreadPool(2);
    var task1 = CompletableFuture.supplyAsync(DateUtil::getInstance,myThreadPool);
    var task2 = CompletableFuture.supplyAsync(DateUtil::getInstance,myThreadPool);
    var instance1 = task1.join();
    var instance2 = task2.join();

    assertNotEquals(instance1, instance2);
    myThreadPool.shutdown();
}

It maybe because CompletableFuture.supplyAsync() could be submitting the task to a threadpool which has only a single available thread to process. So, task2 will only be executed after task1 has complete.

This might happen when you're running the unit tests with other tests. Without knowing more, impossible to tell.

However, you could try creating your own threadpool with enough threads to execute everything in parallel. Try this:

    @Test
    void shouldVerifyThatClassInstantiationIsThreadUnsafe() {
    ExecutorService myThreadPool = Executors.newFixedThreadPool(2);
    var task1 = CompletableFuture.supplyAsync(DateUtil::getInstance,myThreadPool);
    var task2 = CompletableFuture.supplyAsync(DateUtil::getInstance,myThreadPool);
    var instance1 = task1.join();
    var instance2 = task2.join();

    assertNotEquals(instance1, instance2);
    myThreadPool.shutdown();
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文