如何使用Java在多线程环境中进行测试 如何在多线程环境中测试类似的内容。我知道它会失败,因为这个代码不是线程安全的。我只是想知道我怎样才能证明它?创建一堆线程并尝试添加这些不同的线程?出于测试目的,故意未正确编写此代码

如何使用Java在多线程环境中进行测试 如何在多线程环境中测试类似的内容。我知道它会失败,因为这个代码不是线程安全的。我只是想知道我怎样才能证明它?创建一堆线程并尝试添加这些不同的线程?出于测试目的,故意未正确编写此代码,java,multithreading,unit-testing,Java,Multithreading,Unit Testing,公共课堂反应小组经理{ 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 an

公共课堂反应小组经理{

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
private静态HashMap响应\u单元\u DB=
新的HashMap();
/**
* 
*此子程序将新的响应单元添加到数据存储中
*新响应单元必须是有效的响应单元对象,并且其ID必须为
*唯一(即,数据存储中不得已存在)。
* 
*引发异常:Null\u对象\u异常
*/
公共静态无效添加\响应\单元(响应\单元新建\单元)
引发空\u对象\u异常、重复\u项\u异常{
字符串Unit_ID=New_Unit.Unit_ID();
if(新单位==null)
抛出新的Null_对象_异常();
否则,如果(响应单元存在(单元ID))
抛出新的重复项异常(单位ID);
其他的
响应单元数据库put(单元ID,新单元);
}//结束添加\响应\单元

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


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


正如其他人所指出的,您不能编写一个保证失败的测试,因为线程调度可能“刚刚完成”,但如果存在线程安全问题,您可以编写通过概率非常低的测试。例如,您的代码试图禁止数据库中的重复项,但由于线程安全问题,它无法这样做。因此,生成大量线程,让它们都等待
倒计时闩锁或最大化你触发比赛的机会,然后让他们都尝试插入相同的项目。最后,你可以检查(a)除一个线程外,所有线程都看到了一个
重复的\u项目\u异常
,(b)响应单元\u 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);
}  
@测试
public void testIsThreadSafe(){
最终int NUM_迭代次数=100;
对于(int i=0;i{
allThreadsWaitOnThis.wait();
//在这里做一些关于你如何构建的假设
//响应单元
响应单元=新的响应单元(单元ID);
试一试{
经理。添加响应单元(单元);
}捕获(重复项目异常e){
numThreadsSawException.incrementAndGet();
}
});
//释放所有线程
allThreadsWaitOnThis.countdown();
//等待他们全部完成
exec.shutdown();
执行等待终止(10,时间单位:分钟);
assertThat(numThreadsSawException.get()).isEqualTo(NUM_THREADS-1);
}  

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

正如其他人所指出的,您不能编写一个保证失败的测试,因为线程计划可能“刚刚完成”,但如果存在线程安全问题,您可以编写通过概率非常低的测试。例如,您的代码试图禁止数据库中的重复项,但由于线程安全问题,它无法这样做。因此,生成大量线程,让它们都等待
倒计时闩锁或最大化你触发比赛的机会,然后让他们都尝试插入相同的项目。最后,你可以检查(a)除一个线程外,所有线程都看到了一个
重复的\u项目\u异常
,(b)响应单元\u 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);
}  
@测试
public void testIsThreadSafe(){
最终int NUM_迭代次数=100;
对于(int i=0;i