多线程问题在实际编程中多数情况下都很棘手,怎样检测和解决这些问题呢?下面是预防和检测这些问题的一些思路和办法。

以下是我对SO这个问题的理解和翻译。
----------------------------------------------------------------


“我们没有办法重现这个问题, 所以我们修正不了。请提供详细的重现步骤然后我们来修正它”

咦,这句话是不是很熟悉?其实再说这句话的时候你十有八九已经开始怀疑是不是多线程问题搞的鬼了。

那么好,我们该怎样检测和调试多线程问题呢?

众所周知, 多线程/并发问题是臭名昭著的“难重现,难调试”,这也就是为什么你需要在程序设计的时候需要避免和尽可能降低这样问题出现的机率。同样这也就是为什么“不可变对象(immutable object)”是那么珍贵。
试着把“可变的对象(mutable object)”从单线程中分离出来,然后小心地控制“可变的对象”在多线程间的交换。
尝试设计一种对象的“传递(hand-over)机制”而不是“共享(share)”对象。使用”同步并发锁控制对象“来实现同步,把握好同步“并发锁对象控制对象”的粒度,对于需要不同“锁对象”控制的代码不要仅仅使用一个大一统的锁对象来试图控制--也就是说要让这些“并发锁”们各司其职。

总之“降伏”好多线程的最好办法就是好的设计。

-- 死锁( wkipedia

     如果你能够得到死锁问题发生现场的调用堆栈,那么死锁问题是很容易调试的。在实际编程中死锁经常发生在使用不同的顺序获得相同的“并发锁”们。

-- 活锁( wikipedia

     相比于死锁,活锁的解决就要困难些。

竞态条件( wikipedia)引起的问题很难重现,并且很难从代码review中检查出。对于这一类的问题一般的做法就是:

1. 增加测试的频度 ;
2. 在可疑的代码处打tracelog 证明或排除对问题的预判。

对于复杂的系统,找出多线程问题引发问题的原因就更困难些,可以使用类似 JV isualVM,和  remote connect profilers  工具来帮助你分析和解决问题。

同时要注意这些很难重现的问题可能是由诸如CPU的核心数,管道(pipelines),总线带宽等原因引起的。硬件上的差异可能会影响重现这些问题。一些问题可能只会在单核心的CPU上重现,而另一些问题可能只会在多核心上重现。

最后要说的是建议使用系统的并发类库(如java中的   java.util.concurrent 包 )来控制并发问题。

可以使用FindBug来检测一部分Java程序的并发bug,虽然这个工具能够帮助你找到一些bug,但这不是万能的“银弹”。预防bug还是要依靠程序员的努力。