多线程带来哪些问题

终于又回到了JAVA,如果你看先前的为什么我们要考虑并发文章,经过一系列思考后仍然要加入到多线程阵营的话,抱歉,我可能又要给你泼冷水了。

很难编写一个线程安全的程序

线程安全从现象上来描述是在多线程环境中运行结果和单线程运行结果一致。那怎么做到线程安全呢?第一选择避免共享资源,如果避免不了使用共享资源,那么就要保证共享资源的原子性和一致性。

但是在编写代码的时候,是很难做到在多线程中不使用共享资源,而多线程见对共享资源的控制难度比较大。其实Java提供了了最简单的解决并发的方式,性能还不错,就是synchronized,但是这个太简单了,带来的问题就是无法灵活地操作这把锁,比如设置锁的超时时间,立即释放锁,无法通信。有了Lock,Java并发的世界打开了一个新的视角,要编写一个正确的线程安全的程序,对于高手来说也是一项挑战。

调试困难

由于线程执行顺序的不可预知和随机性,多线程的程序难以调试,难以重现。“你的提这个BUG,我重现不了”,这句话是不是很熟悉呢?Java采用抢占式方式对线程进行调度,在这种调度下,有些线程由于CPU分配的时间片较多会执行的时间很长,而有些线程抢占不到CPU的时间片得不到执行。
除了抢占式调度还有协同式调度,协同式调度是线程执行完后主动通知系统切换到其他线程上,从实现上来说协同式调度很简单,不需要线程同步。缺点是一个线程有问题,就会阻塞到那里,因此Java采用了抢占式调度。

死锁

线程间对共享资源的抢夺,更严重的会造成死锁。比如: A线程已经占用资源1的锁,要抢占资源2的锁,而B线程已经占用资源2的锁,要抢占资源1的锁,线程AB互不释放持有的锁,一直等待,就会发生死锁。发生死锁需要满足四个条件,破坏其中任何一个就可以避免死锁,不过Java没有死锁处理机制,不会自动避免死锁。

最后

引入多线程的本意是利用CPU空闲时间提升CPU的利用率,提升性能,然而由于对多线程和操作系统的理解不够深入,会带来额外的问题,这些问题导致多线程安全编程难上加难。

参考

  1. 为什么多线程是个坏主意
  2. Java多线程的调度策略