java 如何对同步方法进行单元测试?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/26967036/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 10:58:00  来源:igfitidea点击:

how to unit test a synchronized method?

javaunit-testingtestingsynchronized

提问by elfar

Say I have such a method:

说我有这样的方法:

synchronized void incrementIndex() {
      index++;
}

I want to unit test this method to see whether index's final value is set correctly if multiple threads simultaneously attempt to increment index. Assuming I don't know about the "synchronized" keyword in the method's declaration (and I only know the contract of the method), how can I do the test?

我想对这个方法进行单元测试,看看如果多个线程同时尝试增加索引,索引的最终值是否设置正确。假设我不知道方法声明中的“synchronized”关键字(并且我只知道方法的契约),我该如何进行测试?

p.s. I am using Mockito for writing test cases if it helps.

ps 如果有帮助,我正在使用 Mockito 编写测试用例。

回答by LeffeBrune

You could test this by having multiple threads execute the method and then asserting that the result is what you would expect. I have my doubts about how effective and reliable this would be. Multithreaded code is notoriously difficult to test and it mostly comes down to careful design. I would definitely recommend adding tests that assert that the methods you expect to by synchronized actually have the synchronized modifier. See an example of both approaches below:

您可以通过让多个线程执行该方法然后断言结果是您所期望的来测试这一点。我怀疑这将是多么有效和可靠。众所周知,多线程代码很难测试,这主要归结为精心设计。我肯定会建议添加测试,以断言您希望通过 synchronized 实现的方法实际上具有 synchronized 修饰符。请参阅以下两种方法的示例:

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import org.junit.Test;

public class SyncTest {
  private final static int NUM_THREADS = 10;
  private final static int NUM_ITERATIONS = 1000;

  @Test
  public void testSynchronized() throws InterruptedException {
    // This test will likely perform differently on different platforms.
    ExecutorService executor = Executors.newFixedThreadPool(NUM_THREADS);
    final Counter sync = new Counter();
    final Counter notSync = new Counter();

    for (int i = 0; i < NUM_THREADS; i++) {
      executor.submit(new Runnable() {
        @Override
        public void run() {
          for (int i = 0; i < NUM_ITERATIONS; i++) {
            sync.incSync();
            notSync.inc();
          }
        }
      });
    }

    executor.shutdown();
    executor.awaitTermination(5, TimeUnit.SECONDS);
    assertThat(sync.getValue(), is(NUM_THREADS * NUM_ITERATIONS));
    assertThat(notSync.getValue(), is(not(NUM_THREADS * NUM_ITERATIONS)));
  }

  @Test
  public void methodIncSyncHasSynchronizedModifier() throws Exception {
    Method m = Counter.class.getMethod("incSync");
    assertThat(Modifier.isSynchronized(m.getModifiers()), is(true)); 
  }

  private static class Counter {
    private int value = 0;

    public synchronized void incSync() {
      value++;
    }

    public void inc() {
      value++;
    }

    public int getValue() {
      return value;
    }
  }
}

回答by bigT

CandiedOrange is correct in his comment to your question. In other words, and given the method you mentioned, you should not worry about threadA calling the method at the same moment threadB is since both calls are writing to index. Had it been something like:

CandiedOrange 在他对您问题的评论中是正确的。换句话说,鉴于您提到的方法,您不应该担心 threadA 在同一时刻调用该方法 threadB 是因为两个调用都在写入 index.html 。如果它是这样的:

void incrementIndex() {
     index++;
     System.out.println(index); // threadB might have written to index
                                // before this statement is executed in threadA
}

threaA calls the method, increments index in the first statement, then attempts to read the value of index in the second statement, by which time threadB might have already made the call to the method and incremented index before threadA reads it and prints it. This is where synchronizedis necessary to avoid such a situation.

threaA 调用该方法,在第一条语句中增加 index,然后尝试在第二条语句中读取 index 的值,此时线程 B 可能已经调用了该方法并在 threadA 读取并打印它之前增加了索引。这是synchronized避免这种情况所必需的地方。

Now, if you still want to test synchronization, and you have access to the method code (or maybe you can do a similar prototype), you may consider something like the following that illustrates how multithreading behaves with synchronized methods:

现在,如果你仍然想测试同步,并且你可以访问方法代码(或者你可以做一个类似的原型),你可以考虑如下所示的内容,说明多线程如何与同步方法一起工作:

public void theMethod(long value, String caller) {
    System.out.println("thread" + caller + " is calling...");
    System.out.println("thread" + caller + " is going to sleep...");

    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("thread" + caller + " woke up!");
}

This should output:

这应该输出:

threadA is calling...
threadA is going to sleep...
threadA woke up!
threadB is calling...
threadB is going to sleep...
threadB woke up!

Without the synchronizedkeyword, the output be:

没有synchronized关键字,输出为:

threadA is calling...
threadA is going to sleep...
threadB is calling...
threadB is going to sleep...
threadA woke up!
threadB woke up!

回答by johnlon

Is. i++. Atomic?

是。我++。原子?

No

So sync is justified if you care about the correctness of your program.

因此,如果您关心程序的正确性,则同步是合理的。

But testing is hard.

但是测试很难。

Visual inspection tells us that the nonatomic increment ooperation is protected and made atomic and all is well as far as we know, but we're don't know anything about the state of the rest of the system.

目视检查告诉我们,非原子增量操作受到保护并成为原子操作,据我们所知,一切都很好,但我们对系统其余部分的状态一无所知。

It is possible to test that a function is sychronised only by its side effects. There is a testable pattern that orgs the code such that you dependency inject the sync rather than using the Jave intrinsic, but if all there is is your original question then I would rely on visual inspection and obvious correctness.

可以测试一个函数是否仅通过其副作用同步。有一个可测试的模式可以组织代码,这样您就可以依赖注入同步而不是使用 Jave 内在函数,但是如果只有您的原始问题,那么我将依靠视觉检查和明显的正确性。