并发测试大致分飞两类:
不发生任何错误的行为某个良好的行为终究会发生性能通过多个方面来衡量:
在为某个并发类设计单元测试时,首先需要执行与测试串行类时相同的分析——找出需要检查的不变性条件和后验条件。
在测试集中包含一组串行测试通常是有帮助的,因为它们有助于在开始分析数据竞争之前就找出与并发性无关的问题。
如果某个方法需要在某些特定条件下阻塞,那么当测试这种行为时,只有当线程不再继续执行时,测试才是成功的。要测试一个方法的阻塞行为,类似于测试一个抛出异常的方法:如果这个这个方法可以正常返回,那么就意味着测试失败。
Thread.getState的返回结果不能用于并发控制,它将限制测试的有效性——其主要作用还是作为调试信息的来源。
在构建对并发类的安全性测试中,需要解决的关键问题在于,要找出那些容易检查的属性,这些属性在发生错误的情况下极有可能失败,同时又不会使得错误检查代码人为地限制并发性。理想情况是,在测试属性中不需要任何同步机制。
这些测试应该放在多处理器的系统上运行,从而进一步测试更多形式的交替运行。然而,CPU的数量越多并不一定会使测试越高效。要最大程度地检测出一些对执行时序敏感的数据竞争,那么测试中的线程数量应该多于CPU数量,这样在任意时刻都会有一些线程在运行,而另一些被交换出去,从而可以检测线程间交替行为的可预测性。
通过一些测量应用程序中内存使用情况的堆检测工具,可以很容易地测试出对内存的不合理占用。
回调函数的执行通常是在对象生命周期的一些已知位置上,并且在这些位置上非常适合判断不变性条件是否被破坏。
在访问共享状态的操作中,使用Thread.yield将产生更多的上下文切换。
性能测试将衡量典型测试用例中的端到端性能和根据经验值来调整各种不同的限制,例如线程数量、缓存容量等。
记录整个运行过程的时间,然后除以总操作的数量,从而得到每次操作的运行时间。采用基于栅栏的计时器。
通过表示任务完成时间的直方图,最能看出服务时间的变动。
有两种策咯可以防止垃圾回收操作对测量结果产生偏差。
通过在同一个JVM中将相同的测试运行多次,可以验证测试方法的有效性。
在真实的应用程序中略微包含一些并行,防止JVM与执行过程特定的信息来生产更优化的代码,使每次产生的代码不同。
并发应用程序可以交替执行两种不同类型的工作:访问共享数据(例如从共享工作队列中取出下一个任务)已经执行线程本地的计算(例如,执行任务,并假设任务本身不会访问共享数据)。根据两种不同类型工作的相关程度,在应用程序中将出现不同程度的竞争,并表现出不同的性能与可伸缩性。
要获得有实际意义的结果,在并发性能测试中应该尽量模拟典型应用程序中的线程本地计算量已经并发协调开销。
要编写有效的性能测试程序,就需要告诉优化器不要将基准测试当作无用代码而优化掉。这就要求在程序中对每个计算结果都要通过某种方式来使用,这种方式不需要同步或者大量的计算。