您必须解决两个基本问题。首先是:如何产生线程冲突?其次,你如何验证你的测试?
第一个很简单。使用大锤子。编写一些能够检测一个线程踩到另一个线程的代码,并在 50 个连续线程上运行该代码 50 次左右。您可以使用倒计时锁存器执行此操作:
public void testForThreadClash() {
final CountDownLatch latch = new CountDownLatch(1);
for (int i=0; i<50; ++i) {
Runnable runner = new Runnable() {
public void run() {
try {
latch.await();
testMethod();
} catch (InterruptedException ie) { }
}
}
new Thread(runner, "TestThread"+i).start();
}
// all threads are waiting on the latch.
latch.countDown(); // release the latch
// all threads are now running concurrently.
}
您的 testMethod() 必须生成一种情况,在这种情况下,如果没有同步块,某个线程将踩到另一个线程的数据。它应该能够检测到这种情况并抛出异常。(上面的代码没有这样做。异常很可能会在其中一个测试线程上生成,您需要放入一种机制来在主线程上检测它,但这是一个单独的问题。我留下了它为简单起见,但您可以使用第二个锁存器来执行此操作,测试可以在异常时过早清除它。)
第二个问题比较棘手,但有一个简单的解决方案。问题是:你怎么知道你的锤子会产生碰撞?当然,你的代码有同步块来防止冲突,所以你不能回答这个问题。但是你可以。只需删除同步的关键字。由于使您的类线程安全的是 synchronized 关键字,因此您可以删除它们并重新运行测试。如果测试有效,它现在将失败。
当我第一次编写上面的代码时,结果证明它是无效的。我从没见过冲突。所以它(还)不是一个有效的测试。但是现在我们知道如何得到第二个问题的明确答案了。我们得到了错误的答案,但我们现在可以修改测试以生成我们正在寻找的失败。这就是我所做的:我只是连续运行了 100 次测试。
for (int j=0; j<100; ++j) {
testForThreadClash();
}
现在我的测试在大约第 20 次迭代时可靠地失败了。这证实了我的测试是有效的。我现在可以恢复 synchronized 关键字并重新运行测试,确信它会告诉我我的类是否是线程安全的。