如何使用Java在多线程环境中测试某些东西

时间:2015-10-28 22:01:05

标签: java multithreading unit-testing

  • 如何在多线程环境中测试类似的内容。我知道它会失败,因为这段代码不是线程安全的。我只是想知道我怎么能证明这一点?创建一堆线程并尝试添加这些不同的线程?由于测试目的,故意不能正确编写此代码!!!

public class Response_Unit_Manager {

private static HashMap<String, Response_Unit> Response_Unit_DB =
        new HashMap<> ();

/**
 * 
 * This subprogram adds a new Response_Unit to the data store.  The
 * new response unit must be valid Response_Unit object and it's ID must be 
 * unique (i.e., must not already exist in the data store.
 * 
 * Exceptions Thrown:  Null_Object_Exception
 */
public static void Add_Response_Unit (Response_Unit New_Unit) 
        throws Null_Object_Exception, Duplicate_Item_Exception {
    String Unit_ID = New_Unit.Unit_ID ();

    if (New_Unit == null)
        throw new Null_Object_Exception ();

    else if (Response_Unit_Exists (Unit_ID))
        throw new Duplicate_Item_Exception (Unit_ID);

    else
        Response_Unit_DB.put (Unit_ID, New_Unit);
} //end Add_Response_Unit

3 个答案:

答案 0 :(得分:0)

运行测试时可能会很幸运,但是失败的代码并不意味着它是线程安全的代码。检查线程安全性的唯一自动方法是使用一些静态分析工具,您可以在方法/类上添加注释并扫描潜在问题。例如,我知道FindBugs支持一些注释并基于它们进行并发检查。您应该能够将此应用于您的单个Tester类。在这个主题上,业界仍有很大的改进空间,但现在有一些例子:

答案 1 :(得分:0)

正如其他人所说,你不能写一个能保证失败的测试,因为线程安排可能“只是工作”,但你可以编写非常传递概率低的测试如果有线程安全问题。例如,您的代码尝试禁止数据库中的重复项,但由于线程安全问题,它无法执行此操作。因此产生了大量的线程,让它们都等待CountdownLatch或其他什么来最大化你触发比赛的机会,然后让他们都尝试插入相同的项目。最后,您可以检查(a)除了一个线程之外的所有线程都看到Duplicate_Item_Exception和(b)Response_Unit_DB只包含一个项目。对于这些类型的测试,您也可以多次运行(在同一测试中),以最大限度地提高触发问题的机会。

以下是一个例子:

@Test
public void testIsThreadSafe() {
   final int NUM_ITERATIONS = 100;
   for(int i = 0; i < NUM_ITERATIONS; ++i) {
       oneIsThreaSafeTest();
   }
}

public void oneIsThreadSafeTest() {
    final int NUM_THREADS = 1000;
    final int UNIT_ID = 1;
    final Response_Unit_Manager manager = new Response_Unit_Manager();
    ExecutorService exec = Executors.newFixedThreadPool(NUM_THREADS);
    CountdownLatch allThreadsWaitOnThis = new CountdownLatch(1);
    AtomicInteger numThreadsSawException = new AtomicInteger(0);

    for (int i = 0; i < NUM_THREADS; ++i) {
        // this is a Java 8 Lambda, if using Java 7 or less you'd use a
        // class that implements Runnable
        exec.submit(() -> {
            allThreadsWaitOnThis.await();
            // making some assumptions here about how you construct
            // a Response_Unit
            Response_Unit unit = new Response_Unit(UNIT_ID);
            try {
                manager.Add_Response_Unit(unit);
            } catch (Duplicate_Item_Exception e) {
              numThreadsSawException.incrementAndGet();
            }
        });

        // release all the threads
        allThreadsWaitOnThis.countdown();

        // wait for them all to finish
        exec.shutdown();
        exec.awaitTermination(10, TimeUnits.MINUTES);

       assertThat(numThreadsSawException.get()).isEqualTo(NUM_THREADS - 1);
}  

您可以为其他潜在的线程安全问题构建类似的测试。

答案 2 :(得分:0)

查找测试错误的最简单方法(如类中包含的错误)是使用Testrunner,例如以下内容:

import java.io.IOException;

import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerResponseContext;
import javax.ws.rs.container.ContainerResponseFilter;
import javax.ws.rs.ext.Provider;

@Provider
public class NoContentToOkResposeFilter implements ContainerResponseFilter {

    @Override
    public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext) throws IOException {
        if (responseContext.getStatus() == 204) {
            responseContext.setStatus(200);
        }
    }
}

使用动态竞赛条件检测工具,如http://vmlens.com,一种轻量级竞赛状态检测器。这将显示以下竞争条件:

enter image description here

堆栈跟踪导致错误。在左侧写入,右侧为读取。

enter image description here

http://vmlens.com适用于eclipse,所以它依赖于你正在使用的ide,如果它对你有用