如何解决多线程程序中的死锁问题

    经常在技术论坛上看到有人虎目含泪地向人哭诉被多线程程序中的死锁问题搞得欲死欲仙,最后扔下狠话:再不用多线程了,要用多进程。
    的确,死锁问题是一个比较令人头痛的问题:难重现,难调试,又相对隐蔽。但是因此把多线程一把拍死还是过于武断了:多线程在数据共享方面比多进程 方便多了;有些高性能场合必须多线程如epoll服务器;很多语言没有提供多进程的直接支持如Java。而且很多强大健壮的服务是用多线程写的(当然多进 程也很多),既然别人能做到,我们肯定也可以做到,怎么能直接举手投降呢?
    死锁是由于不同线程按照不同顺序进行加锁而造成的。如:
线程A:对lock a加锁 => 对lock b加锁 => dosth => 释放lock b => 释放lock a
线程B:对lock b加锁 => 对lock a加锁 => dosth => 释放lock a => 释放lock b
    这样两条线程,就可能发生死锁问题。要避免发生死锁,应该使用同一个顺序进行加锁。嗯。原理很简单,实现起来很困难。一方面人总有疏忽的时候,另 一方面回调函数、设计模式使得代码跳转的体位可以很复杂,也许在你没注意的情况下就犯了天条,然后就只能在下着雨的死锁夜,愤怒的咆哮啊有木有。所以呢应该由计算机来帮我们进行检验。我实现了一个可重入的带优先级的锁,每个锁均带有一个全局优先级,然后每个线程必须按照优先级从小到大的顺序进行加锁,解锁则反过来。比如有两个锁,优先级是1,2。 则:
lock 1 - lock 2 (OK)
lock 2 - lock 1 (ERROR:加锁顺序错)
lock 1 - lock 2 - lock1 - lock2 (OK)
lock 1 - lock 2 - unlock2 - unlock 1 (OK)
lock 1 - lock 2 - unlock1 (ERROR:解锁顺序错)
lock 1 - lock 2 - lock1 - unlock1 - unlock2 - unlock1 (OK)
lock 1 - lock 2 - lock1 - unlock1 - unlock1 (ERROR:解锁顺序错)
lock 1 - lock 2 - lock1 - unlock2 - unlock1 - unlock1 (OK)
注意:当已经持有某锁的时候,反复加锁总是成功。至于解锁,貌似有些奇怪,其实是为了效率考虑——如果保存所有加锁的细节,要求解锁顺序正好对偶当然逻辑更为严密,自己取舍吧。
      这回代码是用Java写的,想看C++实现的移步:http://blog.sina.com.cn/s/blog_48d4cf2d0100mx4n.html
==============================代码的分割线=============================
import java.util.Stack;

public class PriorityLock {

      private int pri = -1; // 锁对象的优先级,必须严格按照从小到大顺序请求锁,否则lock的时候抛异常
      private long tid = -1; // 当前持有此锁的线程ID
      private int lock_times = 0; // 持有该锁的次数

      private static final ThreadLocal<Stack<Integer>> lstacks = new ThreadLocal<Stack<Integer>>();

      public PriorityLock(int pri_) {
            if (pri_ < 0) {
                  throw new RuntimeException("pri should be positive");
            }
            this.pri = pri_;
      }

      public synchronized void lock() throws InterruptedException {
            long currentid = Thread.currentThread().getId();
            if (tid == currentid) { // 允许重复加锁
                  ++lock_times;
                  return;
            }
            Stack<Integer> stack = getLockStack();
            if (!stack.empty() && stack.lastElement() >= pri) { // 当前持有的锁优先级大于等于新申请的锁,错误
                  throw new RuntimeException("lock in wrong order");
            } else {
                  while (tid != -1) { // 锁-while-等-干-通,五字真言还记得吗
                        wait();
                  }
                  tid = currentid;
                  lock_times = 1;
                  stack.push(pri);
            }
      }

      public synchronized void unlock() {
            long currentid = Thread.currentThread().getId();
            if (currentid != tid) {
                  throw new RuntimeException("unlock in bad manner"); // 斯巴达了,遇到不按规则玩的
            }
            if (lock_times > 1) {
                  --lock_times;
            } else {
                  Stack<Integer> stack = getLockStack();
                  if (stack.empty() || stack.lastElement() != pri) {
                        throw new RuntimeException("unlock in bad manner"); // 再次斯巴达
                  }
                  stack.pop();
                  tid = -1;
                  lock_times = 0;
                  notifyAll();
            }
      }

      private static Stack<Integer> getLockStack() {
            Stack<Integer> stack = lstacks.get();
            if (stack == null) {
                  stack = new Stack<Integer>();
                  lstacks.set(stack);
            }
            return stack;
      }

}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值