如何使用 Java 在多线程环境中测试某些东西 [重复]
Posted
技术标签:
【中文标题】如何使用 Java 在多线程环境中测试某些东西 [重复]【英文标题】:How to test something in Multi-Threaded Environment using Java [duplicate] 【发布时间】:2016-01-28 21:28:25 【问题描述】: 如何在多线程环境中测试类似的东西。我知道它会失败,因为这段代码不是线程安全的。我只想知道如何证明?创建一堆线程并尝试添加这些不同的线程?出于测试目的,此代码故意没有正确编写!!!公共类 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
【问题讨论】:
您希望它失败吗?交付它。 @MartinJames 哈哈 .. 我们以前从未使用过线程。我们只是需要一些帮助来证明这段代码不是线程安全的。如果您能在这里为我们提供帮助,我将不胜感激 :) 你可能会幸运地看到失败,但不失败的代码并不意味着它是线程安全的代码。检查线程安全性的唯一自动化方法是使用一些静态分析工具,这些工具可让您在方法/类上添加注释并扫描潜在问题。 @mvd 感谢您的回复。但不幸的是,我们所拥有的只是一个带有 main 方法的测试器类。您认为我们可以仅使用 main 方法来证明我们的观点吗?再次感谢... 【参考方案1】:运行测试时您可能会很幸运并看到失败,但未失败的代码并不意味着它是线程安全的代码。检查线程安全性的唯一自动化方法是使用一些静态分析工具,这些工具可让您在方法/类上添加注释并扫描潜在问题。例如,我知道 FindBugs 支持一些注释并基于它们进行并发检查。您应该能够将其应用于您的单个 Tester 类。业界在这个话题上仍有很大的改进空间,但这里有一些当前的例子:
http://robertfeldt.net/publications/grahn_2010_comparing_static_analysis_tools_for_concurrency_bugs.pdf http://homepages.inf.ed.ac.uk/dts/students/spathoulas/spathoulas.pdf【讨论】:
【参考方案2】:正如其他人所指出的,您不能编写保证失败的测试,因为线程调度可能“刚刚解决”,但您可以编写通过概率非常低的测试如果存在线程安全问题。例如,您的代码尝试禁止数据库中的重复项,但由于线程安全问题,它不能这样做。因此,产生大量线程,让它们都等待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);
您可以针对其他潜在的线程安全问题构建类似的测试。
【讨论】:
【参考方案3】:查找测试错误的最简单方法(例如您的课程中包含的错误)是使用 Testrunner,例如以下:
package com.anarsoft.mit;
import java.util.concurrent.atomic.AtomicInteger;
public class Test_Response_Unit_Manager implements Runnable
private final AtomicInteger threadCount = new AtomicInteger();
public void test() throws Exception
for(int i = 0; i < 2 ;i++)
Thread thread = new Thread(this, "Thread " + i);
this.threadCount.incrementAndGet();
thread.start();
while( this.threadCount.get() > 0 )
Thread.sleep(1000);
Thread.sleep(10 * 1000);
public void run()
exec();
threadCount.decrementAndGet();
protected void exec()
Response_Unit_Manager.Add_Response_Unit(new Response_Unit(Thread.currentThread().getId()));
public static void main(String[] args) throws Exception
(new Test_Response_Unit_Manager()).test();
并使用动态竞争条件检测工具,如 http://vmlens.com,一个轻量级竞争条件检测器。这将显示以下竞争条件:
还有导致错误的堆栈跟踪。左边是写,右边是读。
http://vmlens.com 与 eclipse 一起使用,所以它取决于你使用的 ide,如果它对你有用
【讨论】:
以上是关于如何使用 Java 在多线程环境中测试某些东西 [重复]的主要内容,如果未能解决你的问题,请参考以下文章