java中常见的死锁以及解决方法代码

下面是Java中常见的死锁以及解决方法的完整攻略。

什么是死锁?

死锁是指在并发编程中,两个或多个线程互相持有对方需要的资源,从而造成它们都无法继续执行的情况。此时,程序会进入一个死循环状态,无法正常运行,也无法进行下一步操作。

常见的死锁场景

以下是一些常见的导致死锁的场景:

1. 多个线程竞争同一资源

多个线程同时竞争同一个资源,如果每个线程都持有该资源的一部分,就有可能会造成死锁。

示例代码

public class DeadLockDemo implements Runnable {

    private final Object lock1 = new Object();
    private final Object lock2 = new Object();

    @Override
    public void run() {
        synchronized (lock1) {
            System.out.println(Thread.currentThread().getName() + " 获取到 lock1");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (lock2) {
                System.out.println(Thread.currentThread().getName() + " 获取到 lock2");
            }
        }
    }

    public static void main(String[] args) {
        DeadLockDemo demo = new DeadLockDemo();
        Thread t1 = new Thread(demo, "Thread1");
        Thread t2 = new Thread(demo, "Thread2");
        t1.start();
        t2.start();
    }
}

上述示例中,线程1获取了lock1的锁,然后休眠一秒钟,之后尝试获取lock2的锁;而线程2则相反,它先获取lock2的锁,然后尝试获取lock1的锁。这样就容易导致死锁。

2. 循环等待

每个线程都在等待其他线程释放它所需要的资源,就会造成循环等待的情况,最终导致死锁。

示例代码

public class DeadLockDemo2 {

    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println(Thread.currentThread().getName() + " 获取到 lock1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println(Thread.currentThread().getName() + " 获取到 lock2");
                }
            }
        }, "Thread1");

        Thread t2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println(Thread.currentThread().getName() + " 获取到 lock2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println(Thread.currentThread().getName() + " 获取到 lock1");
                }
            }
        }, "Thread2");

        t1.start();
        t2.start();
    }
}

上述示例中,线程1获取了lock1的锁,休眠一秒钟之后,尝试获取lock2的锁;而线程2则相反,它先获取lock2的锁,然后尝试获取lock1的锁。这样就容易导致死锁。

解决死锁的方法

针对上述出现死锁的情况,有以下几种常见解决方法:

1. 避免嵌套加锁

尽可能避免嵌套加锁,如果必须要嵌套加锁,那么应该尽可能保证所有线程都按照相同的顺序获取锁,即先获取锁1,再获取锁2。

示例代码

public class NoDeadLockDemo implements Runnable {

    private final Object lock1 = new Object();
    private final Object lock2 = new Object();
    private final boolean flag;

    public NoDeadLockDemo(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if (flag) {
            synchronized (lock1) {
                System.out.println(Thread.currentThread().getName() + " 获取到 lock1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println(Thread.currentThread().getName() + " 获取到 lock2");
                }
            }
        } else {
            synchronized (lock2) {
                System.out.println(Thread.currentThread().getName() + " 获取到 lock2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println(Thread.currentThread().getName() + " 获取到 lock1");
                }
            }
        }
    }

    public static void main(String[] args) {
        NoDeadLockDemo demo1 = new NoDeadLockDemo(true);
        NoDeadLockDemo demo2 = new NoDeadLockDemo(false);
        Thread t1 = new Thread(demo1, "Thread1");
        Thread t2 = new Thread(demo2, "Thread2");
        t1.start();
        t2.start();
    }
}

上述示例中,我们把锁分成了两个,每个线程只获取一个锁。如果需要获取两个锁,按照相同的顺序来获取锁,就不会出现死锁。

2. 使用锁的超时机制

使用 tryLock() 方法获取锁,设置超时时间。当在指定的时间段内没有获取到所需要的锁时,立即释放已经获得的锁。

示例代码

public class TimeOutDeadLockDemo {

    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                if (lock1.equals(lock2)) {
                    System.out.println(Thread.currentThread().getName() + " 获得了所有的锁");
                } else {
                    while (true) {
                        boolean b1 = false, b2 = false;
                        try {
                            b1 = acquire(lock1, 100);
                            b2 = acquire(lock2, 100);
                            if (b1 && b2) {
                                System.out.println(Thread.currentThread().getName() + " 获得了所有的锁");
                                break;
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            if (b1) {
                                release(lock1);
                            }
                            if (b2) {
                                release(lock2);
                            }
                        }
                    }
                }
            } finally {
                System.out.println(Thread.currentThread().getName() + " 退出");
            }
        }, "Thread1");

        Thread t2 = new Thread(() -> {
            try {
                if (lock2.equals(lock1)) {
                    System.out.println(Thread.currentThread().getName() + " 获得了所有的锁");
                } else {
                    while (true) {
                        boolean b1 = false, b2 = false;
                        try {
                            b1 = acquire(lock2, 200);
                            b2 = acquire(lock1, 200);
                            if (b1 && b2) {
                                System.out.println(Thread.currentThread().getName() + " 获得了所有的锁");
                                break;
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        } finally {
                            if (b1) {
                                release(lock2);
                            }
                            if (b2) {
                                release(lock1);
                            }
                        }
                    }
                }
            } finally {
                System.out.println(Thread.currentThread().getName() + " 退出");
            }
        }, "Thread2");

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        System.out.println("主线程退出");
    }

    public static boolean acquire(Object lock, long timeout) throws InterruptedException {
        long start = System.currentTimeMillis();
        long end = start + timeout;
        while (System.currentTimeMillis() < end) {
            if (Thread.holdsLock(lock)) {
                return true;
            }
            if (Thread.currentThread().isInterrupted()) {
                throw new InterruptedException();
            }
            if (lock instanceof ReentrantLock) {
                ReentrantLock reentrantLock = (ReentrantLock) lock;
                if (reentrantLock.tryLock(timeout - (System.currentTimeMillis() - start), TimeUnit.MILLISECONDS)) {
                    return true;
                }
            } else {
                synchronized (lock) {
                    if (Thread.holdsLock(lock)) {
                        return true;
                    }
                    lock.wait(timeout - (System.currentTimeMillis() - start));
                }
            }
        }
        return false;
    }

    public static void release(Object lock) {
        if (lock instanceof ReentrantLock) {
            ReentrantLock reentrantLock = (ReentrantLock) lock;
            if (reentrantLock.isHeldByCurrentThread()) {
                reentrantLock.unlock();
            }
        } else {
            synchronized (lock) {
                lock.notifyAll();
            }
        }
    }
}

上述示例中,我们使用了 tryLock() 方法获取锁,并设置了超时时间。如果在指定时间内未能获取到锁,就会立即释放已经获取的锁,避免死锁。同时,我们定义了 acquire()release() 两个方法来封装获取锁和释放锁的操作,可以根据实际情况来调用不同的方法。

总结

死锁是多线程编程中很容易出现的问题,我们必须采取一些措施来避免死锁的发生。一般来说,可以通过避免嵌套锁以及使用锁的超时机制等方式来解决死锁问题。但在实际开发中,还需要根据具体情况来选择最合适的方案。

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:java中常见的死锁以及解决方法代码 - Python技术站

(0)
上一篇 2023年5月23日
下一篇 2023年5月23日

相关文章

  • Spring Boot应用监控的实战教程

    SpringBoot应用监控的实战教程 SpringBoot应用监控是确保应用程序保持健康运行的重要方式。本文将介绍如何使用开源监控组件Spring Boot Admin和Micrometer对SpringBoot应用进行监控。 Spring Boot Admin Spring Boot Admin是一个开源的监控组件,它提供了可视化的界面,方便您查看Spr…

    Java 2023年5月15日
    00
  • 如何将javaweb项目部署到linux下

    下面是如何将Java Web项目部署到Linux下的完整攻略。 步骤一:准备工作 在将Java Web项目部署到Linux下之前,我们需要准备以下工具: 一台运行Linux操作系统的服务器 Java开发包(JDK) Tomcat服务器 Maven构建工具 Git版本控制工具 步骤二:编写代码并打包 在准备好工具之后,我们需要编写Java Web项目的代码并将…

    Java 2023年5月20日
    00
  • jQuery EasyUI 布局之动态添加tabs标签页

    jQuery EasyUI是一个基于jQuery的UI插件集,提供了多种易用且功能强大的UI组件,其中包含布局组件,如Accordion、Tabs、Panel、Layout等。本文将详细讲解如何使用jQuery EasyUI布局组件中的Tabs,并通过动态添加Tabs标签页的方式来实现内容与标签页之间的切换。 准备工作 首先,需要引入jQuery EasyU…

    Java 2023年6月15日
    00
  • 详解如何探测小程序返回到webview页面

    探测小程序返回到webview页面主要有两个部分:小程序侧的操作和webview侧的操作。 小程序侧的操作 步骤一:调用小程序JSAPI 小程序提供了navigateBackMiniProgram的JSAPI,可以在小程序内部调用,从而返回webview页面。 wx.navigateBackMiniProgram({ success: function() …

    Java 2023年5月23日
    00
  • Java设计模式之java状态模式详解

    Java设计模式之Java状态模式详解 简介 Java状态模式是一个行为型设计模式,其可以通过改变对象内部的状态来改变对象的行为。这个模式可以在对象行为随状态改变的场景中实现。 适用场景 适用场景如下所示: 对于一个对象的某个行为,实现多种状态,这些状态之间能够相互转换。 当一个对象的行为依赖于它的状态,并且它需要在运行时根据状态改变其行为时。 模式结构 J…

    Java 2023年5月26日
    00
  • springboot使用消息中间件

    Spring Boot是一个快速构建应用程序的框架,它提供了许多常用的功能,如Web、数据访问、安全等。在Spring Boot中,我们可以使用消息中间件来实现异步通信,提高应用程序的性能和可伸缩性。以下是Spring Boot使用消息中间件的完整攻略: 添加消息中间件依赖 在Spring Boot中,我们可以使用Maven或Gradle来添加消息中间件依赖…

    Java 2023年5月15日
    00
  • Spring中使用腾讯云发送短信验证码的实现示例

    下面我将为您详细讲解在Spring中使用腾讯云发送短信验证码的实现示例,并提供两个示例给您参考。 腾讯云SMS SDK介绍 在Spring中使用腾讯云发送短信验证码,需要使用腾讯云的SMS SDK。腾讯云的SMS SDK是专为开发人员和企业用户量身打造的短信发送解决方案,它提供了多种方式发送短信,包含了API接口、SDK、控制台等多种方式,并且可以满足不同场…

    Java 2023年5月20日
    00
  • Java几个实例带你进阶升华上篇

    这里是完整的 “Java几个实例带你进阶升华上篇” 技术攻略。 1. 概述 本篇攻略主要介绍了 Java 编程语言中的一些进阶技术,采用实例讲解的方式帮助读者深入了解相关技术。 2. 内容 以下是本篇攻略的主要内容: 2.1 数据结构 Java 中常用的数据结构包括数组、链表、栈、队列、哈希表、二叉树等。这些数据结构是程序设计中必不可少的基础。 示例一:实现…

    Java 2023年5月19日
    00
合作推广
合作推广
分享本页
返回顶部