第12章 并发程序的测试

测试结果表明,LinkedBlockingQueue的可伸缩性要高于ArrayBlockingQueue。初看起来,这个结果有些奇怪:链表队列在每次插入元素时,都必须分配一个链表节点对象,这似乎比基于数组的队列执行了更多的工作。然而,虽然它拥有更好的内存分配与GC等开销,但与基于数组的队列相比,链表队列的put和take等方法支持并发性更高的访问,因为一些优化后的链接队列算法能将队列头节点的更新操作与尾节点的更新操作分离开来。由于内存分配操作通常是线程本地的,因此如果算法能通过多执行一些内存分配操作来降低竞争程度,那么这种算法通常具有更高的可伸缩性。(这种情况再次证明了,基于传统性能调优的直觉与提升可伸缩性的实际需求是背道而驰的)

要编写有效的性能测试程序,就需要告诉优化器不要将基准测试当作无用代码而优化掉。这就要求在程序中对每个计算结果都要通过某种方式来使用,这种方式不需要同步或者大量的计算。

有一个简单的技巧可以避免运算被优化掉而又不会引入过高的开销:即计算某个派生对象中域的散列值,并将它与一个任意值进行比较,例如System.nanoTime的当前值,如果二者碰巧相等,那么就输出一个无用并且可被忽略的消息:

    if (foo.x.hashCode() == System.nanoTime()) {
        System.out.println(" ");
    }

这个比较操作很少会成功,即使成功了,它的唯一作用也就是在输出中插入一个无害的空字符。(print方法中把输出结果缓存起来,并直到调用println才真正地执行输出操作,因此即使hashCode和System.nanotime的返回值碰巧相等,也不会真正地执行I/O操作)。

不仅每个计算结果都应该被使用,而且还应该是不可预测的。否则一个智能的动态优化编译器将用预先计算的结果来代替计算过程。

测试的目标不是更多地发现错误,而是提高代码能按照预期方式工作的可信度。由于找出所有的错误是不现实的,所以质量保证(Quality Assurance,QA)的目标应该是在给定的测试资源下实现最高的可信度。测试是一种非常重要的首选,但并不是唯一可用的QA方法。

还有其他一些QA方法,它们在找出某些类型的错误时非常高效,而在找出其他类型的错误时则相对低效。通过使用一些补充的测试方法,例如代码审查和静态分析等,可以获得比在使用任何单一方法更多的可信度。

原文地址:https://www.cnblogs.com/a0000/p/4903453.html