Java Thread系列(七)死锁

Java Thread系列(七)死锁

当线程需要同时持有多个锁时,有可能产生死锁。考虑如下情形:

线程 A 当前持有互斥所锁 lock1,线程 B 当前持有互斥锁 lock2。接下来,当线程 A 仍然持有 lock1 时,它试图获取 lock2,因为线程 B 正持有 lock2,因此线程 A 会阻塞等待线程 B 对 lock2 的释放。如果此时线程 B 在持有 lock2 的时候,也在试图获取 lock1,因为线程 A 正持有 lock1,因此线程 B 会阻塞等待 A 对 lock1 的释放。二者都在等待对方所持有锁的释放,而二者却又都没释放自己所持有的锁,这时二者便会一直阻塞下去。这种情形称为死锁。

下面给出一个两个线程间产生死锁的示例,如下:

/**
 * 多个线程挣抢同一份资源,容易造成死锁
 * @author: leigang
 * @version: 2018-05-05
 */
public class DeadThread {

    public static void main(String[] args) {
        Object goods = new Object();
        Object money = new Object();

        new Test1(goods, money).start();
        new Test2(goods, money).start();

    }

    static class Test1 extends Thread {
        private Object goods;
        private Object money;

        public Test1(Object goods, Object money) {
            this.goods = goods;
            this.money = money;
        }

        @Override
        public void run() {
            while (true) {
                test();
            }
        }

        public void test() {
            synchronized (goods) {  // (1)
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (money) {
                }
            }
            System.out.println(Thread.currentThread().getName() + "先交货再给钱");
        }
    }

    static class Test2 extends Thread {
        private Object goods;
        private Object money;

        public Test2(Object goods, Object money) {
            this.goods = goods;
            this.money = money;
        }

        @Override
        public void run() {
            while (true) {
                test();
            }
        }

        public void test() {
            synchronized (money) {  // (2)
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (goods) {
                }
            }
            System.out.println(Thread.currentThread().getName() + "先给钱再交货");
        }
    }
}

说明:线程 1 持有 goods 锁并打算获取 money 锁,但线程 2 已经持有 money 锁并打算获取 goods 锁,由于这两个锁都被锁住,所以这两个线程都不能获取相应的锁,这样就产生了死锁。

大部分代码并不容易产生死锁,死锁可能在代码中隐藏相当长的时间,等待不常见的条件地发生,但即使是很小的概率,一旦发生,便可能造成毁灭性的破坏。避免死锁是一件困难的事,遵循以下原则有助于规避死锁:

1、只在必要的最短时间内持有锁,考虑使用同步语句块代替整个同步方法;

2、尽量编写不在同一时刻需要持有多个锁的代码,如果不可避免,则确保线程持有第二个锁的时间尽量短暂;

3、创建和使用一个大锁来代替若干小锁,并把这个锁用于互斥,而不是用作单个对象的对象级别锁;

既然多个线程操作同一个对象容易产生死锁,那么如何解决这个问题呢?一个经典的解决方案是 生产者-消费者模式

参考:

《Java 并发编程》:http://www.importnew.com/20638.html


每天用心记录一点点。内容也许不重要,但习惯很重要!

猜你喜欢

转载自www.cnblogs.com/binarylei/p/8999701.html