我有简单的应用程序,模拟从一个帐户到另一个帐户的汇款.我想写一个测试,表明它不是线程安全的.
线程有可能以这样的方式进行,即传输将进行两次.两个线程场景:
> acc1 = 1000 $
> acc2 = 0 $
>转600美元
> T1:获得acc1余额(1000)
> T2:获得acc1余额(1000)
> T1:从account1减去600 $(400)
> T2:从account2(400)减去600 $
> T1:将acc2提高600 $(600)
> T2:将acc2提高600 $(1200)
目前我的应用程序不支持多线程,它应该失败,现在对我来说没问题.我能够使用调试器模拟错误.但是,当我进行线程测试时,它总是成功的.我试过不同数量的线程,睡眠,callabletasks
@Test
public void testTransfer() throws AccountNotFoundException,NotEnoughMoneyException,InterruptedException {
Callable
这是汇款代码:
public void transferMoney(String accountFrom,String accountTo,BigDecimal amount) throws AccountNotFoundException,NotEnoughMoneyException {
Account fromAccount = getAccountByNumber(accountFrom);
Account toAccount = getAccountByNumber(accountTo);
if (isBalanceSufficient(amount,fromAccount)) {
//TODO this should be thread safe and transactional
BigDecimal fromNewAmount = fromAccount.getBalance().subtract(amount);
fromAccount.setBalance(fromNewAmount);
// it's possible to fail junits with sleep but I dont want it in code obvIoUsly
// Random random = new Random();
// try {
// Thread.sleep(random.nextInt(100));
// } catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
BigDecimal toNewAmount = toAccount.getBalance().add(amount);
toAccount.setBalance(toNewAmount);
} else {
throw new NotEnoughMoneyException("Balance on account: " + fromAccount.getNumber() + " is not sufficient to transfer: " + amount);//TODO add currency
}
}
但也很难引发线程错误.多线程的第一条规则是你不能通过练习(例如单元)测试证明(或容易反驳)代码是线程安全的.
不安全的代码可以执行十亿次而不会出错.在某些平台上,它实际上可能是线程安全的,但在其他平台上则一致地失败.当您开始使用线程时,所有Java代码在所有平台上的行为相同的想法都会失控.
balance+=transaction;
但它也是如此小的一段代码,它可能在某些平台上是安全的,或者只是如此之快,它运行数十亿次而没有错误.
int temp=balance;
Thread.sleep(1000);
balance=temp+transaction;
很有可能最终在大多数平台上失败.但那又怎么样?
它没有证明原始的代码行,有时会引入延迟掩盖问题,尤其是其他地方.
验证或使多线程代码无效的唯一方法是静态分析和对语言保证的良好了解.
您可以尝试以高负载运行(比如)您的平台实际并行运行的线程数量的两倍,并对您很有可能引发问题的底层代码进行一些猜测.但有些错误可能只发生在低负载或其间的任何负载.
请记住,如果您修改代码重新测试并且它工作正常,您就没有证明什么.我不是说你不应该做最后检查这样的测试.
但是,从来没有想过单元测试有助于证明多线程的可靠性,它们有助于单线程.这尤其是因为不同的平台可能具有不同的配置(例如,进程数,高速缓存级别,核心)并且经历不同级别的负载.