如何使用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类中。在这个主题上,业界仍有很大的改进空间,但下面是一些当前的示例:
倒计时闩锁或最大化你触发比赛的机会,然后让他们都尝试插入相同的项目。最后,你可以检查(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