Java中线程的死锁问题


  

引言:

java开发中当某些逻辑设计得不合理时,将会出现“线程死锁”的问题。

线程死锁

  1. 不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁
  2. 出现死锁后,不会出现异常,不会出现提示,只是所有的线程都处于阻塞状态,无法继续。当我们使用Java中的同步机制时要避免出现死锁

线程死锁演示 1:

public class DeadLockTest {
    public static void main(String[] args) {
        StringBuffer s1 = new StringBuffer();
        StringBuffer s2 = new StringBuffer();

        new Thread() {
            @Override
            public void run() {

                synchronized (s1) {
                    s1.append("a");
                    s2.append("1");

                    try {
                        sleep(1000);//强行休眠,提高出现死锁的概率,达到演示效果
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (s2) {
                        s1.append("b");
                        s2.append("2");

                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }.start();

        new Thread(new Runnable() {
            @Override
            public void run() {

                synchronized (s2) {
                    s1.append("c");
                    s2.append("3");

                    try {
                        Thread.sleep(1000);//强行睡眠,提高出现死锁的概率,达到演示效果
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    synchronized (s1) {
                        s1.append("d");
                        s2.append("4");

                        System.out.println(s1);
                        System.out.println(s2);
                    }
                }
            }
        }).start();
    }
}

  第一个线程握住s1锁,等待其他线程释放s2锁;第二个线程握住s2锁,等待其他线程释放s1锁。两个线程都在等待对方放弃自己需要的同步资源,出现死锁。

线程死锁演示 2:

class A {
    public synchronized void foo(B b) {//非静态方法同步监视器为this:A类的对象a
        System.out.println("当前线程名:" + Thread.currentThread().getName() + " 进入了A实例的foo()方法");

        try {
            Thread.sleep(1000);//强行休眠,提高出现死锁的概率,达到演示效果
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("当前线程名:" + Thread.currentThread().getName() + " 企图调用B实例的last()方法");
        b.last();
    }

    public synchronized void last() {//非静态方法同步监视器为this:A类的对象a
        System.out.println("进入了A类的last()方法内部");
    }
}

class B {
    public synchronized void bar(A a) {//非静态方法同步监视器为this:B类的对象b
        System.out.println("当前线程名:" + Thread.currentThread().getName() + " 进入了B实例的bar()方法");

        try {
            Thread.sleep(1000);//强行休眠,提高出现死锁的概率,达到演示效果
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("当前线程名:" + Thread.currentThread().getName() + " 企图调用A实例的last()方法");
        a.last();
    }

    public synchronized void last() {//非静态方法同步监视器为this:B类的对象b
        System.out.println("进入了B类的last()方法内部");
    }
}

class DeadLock implements Runnable {
    A a = new A();
    B b = new B();

    public void init() {
        Thread.currentThread().setName("主线程");
        a.foo(b);//调用a对象的foo()方法
        System.out.println("主线程结束");
    }

    @Override
    public void run() {
        Thread.currentThread().setName("副线程");
        b.bar(a);//调用b对象的bar()方法
        System.out.println("副线程结束");
    }
}

public class DeadLockTest {
    public static void main(String[] args) {
        DeadLock dl = new DeadLock();
        new Thread(dl).start();

        dl.init();
    }
}

减少死锁的出现,自己体会

class A {
    public synchronized void foo(B b) {//非静态方法同步监视器为this:A类的对象a
        System.out.println("当前线程名:" + Thread.currentThread().getName() + " 进入了A实例的foo()方法");
        System.out.println("当前线程名:" + Thread.currentThread().getName() + " 企图调用B实例的last()方法");
        b.last();
    }

    public synchronized void last() {//非静态方法同步监视器为this:A类的对象a
        System.out.println("进入了A类的last()方法内部");
    }
}

class B {
    public synchronized void bar(A a) {//非静态方法同步监视器为this:B类的对象b
        System.out.println("当前线程名:" + Thread.currentThread().getName() + " 进入了B实例的bar()方法");
        System.out.println("当前线程名:" + Thread.currentThread().getName() + " 企图调用A实例的last()方法");
        a.last();
    }

    public synchronized void last() {//非静态方法同步监视器为this:B类的对象b
        System.out.println("进入了B类的last()方法内部");
    }
}

class DeadLock implements Runnable {
    A a = new A();
    B b = new B();

    public void init() {
        Thread.currentThread().setName("主线程");
        //调用a对象的foo()方法
        a.foo(b);
        System.out.println("主线程结束");
    }

    @Override
    public void run() {
        Thread.currentThread().setName("副线程");
        //调用b对象的bar()方法
        b.bar(a);
        System.out.println("副线程结束");
    }
}

public class DeadLockTest {
    public static void main(String[] args) {
        DeadLock dl = new DeadLock();
        new Thread(dl).start();

        //增加"不出现死锁"的概率
        try {
            Thread.sleep(1000);//主线程休眠
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        dl.init();
    }
}
//可能的输出
/**
当前线程名:副线程 进入了B实例的bar()方法
当前线程名:副线程 企图调用A实例的last()方法
进入了A类的last()方法内部
副线程结束
当前线程名:主线程 进入了A实例的foo()方法
当前线程名:主线程 企图调用B实例的last()方法
进入了B类的last()方法内部
主线程结束
**/

解决办法

  1. 专门的算法、原则
  2. 尽量减少同步资源的定义
  3. 尽量避免嵌套同步

文章作者: YangChongZhi
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 YangChongZhi !
评论
 上一篇
Java中使用Lock锁解决线程安全问题 Java中使用Lock锁解决线程安全问题
   引言: java中的同步机制除了“同步代码块”和“同步方法”外,还提供了更强大的线程同步机制。通过显式定义同步锁对象来实现同步,同步锁使用Lock对象充当。 Lock 锁 从JDK 5.0开始,Java提供了更强大的线程同步机制——
2021-01-06
下一篇 
Java中实现线程安全的单例模式 Java中实现线程安全的单例模式
   引言: 使用java中的同步机制可以将单例模式中的“懒汉式”改写为线程安全的。而“饿汉式”单例模型本身就是线程安全的。 本身就是线程安全的“饿汉式”单例模式class Bank { //1.私有化类的构造器
2021-01-06
  目录