繁体   English   中英

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

[英]how to unit test a synchronized method?

说我有这样一种方法:

synchronized void incrementIndex() {
      index++;
}

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

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

您可以通过让多个线程执行该方法,然后断言结果是否符合您的预期来对此进行测试。 我对这将是多么有效和可靠感到怀疑。 众所周知,多线程代码很难测试,并且大多归结为精心设计。 我绝对会建议添加一些测试,这些测试断言您期望通过同步进行的方法实际上具有synced修饰符。 请参阅以下两种方法的示例:

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;
    }
  }
}

CandiedOrange对您的问题的评论正确。 换句话说,给定您提到的方法,您不必担心threadA在同一时刻调用方法的原因在于threadB是因为两个调用都写入索引。 如果是这样的话:

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

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

现在,如果您仍然想测试同步,并且可以访问方法代码(或者您可以做一个类似的原型),则可以考虑以下内容,这些内容说明了同步方法中多线程的行为:

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!");
}

这应该输出:

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

如果没有synchronized关键字,则输出为:

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

是。 我++。 原子?

没有

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

但是测试很难。

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM