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日

相关文章

  • 精通Java泛型的使用与原理

    精通Java泛型的使用与原理 什么是泛型? 泛型可以将数据类型作为数据参数传递,这个数据类型可以被用在类、接口和方法中。泛型的引入使得程序具有更好的类型安全性、代码复用性和可读性。 泛型的基本语法 泛型的基本语法为: class 类名<泛型参数,泛型参数,…>{ } interface 接口名<泛型参数,泛型参数,…>{ } …

    Java 2023年5月26日
    00
  • Spring Security 控制授权的方法

    Spring Security 是用来保护 Spring 应用程序的框架。其中一个主要的功能就是控制授权。 在 Spring Security 中,授权可以通过一些方法来实现。以下是一些控制授权的方法: 1. 基于角色的授权 基于角色的授权是一种最常用的方法,它根据用户的角色来判断是否允许执行某个操作。在 Spring Security 中,可以使用 @Pr…

    Java 2023年5月20日
    00
  • java中Class类的基础知识点及实例

    Java中Class类的基础知识点及实例 Class类的概念 Class 类是 Java 中用于描述类类型的类,它是所有类、接口、数组在内存中的一个表示。Class 对象是在类被加载的时候创建的,它保存了类的相关信息,例如类的名称、类的成员变量、类的方法等。 通过 Class 对象,我们可以对类进行一些操作,例如创建该类的实例、获取它所包含的方法以及构造函数…

    Java 2023年5月26日
    00
  • Springboot es包版本异常解决方案

    下面是“Springboot es包版本异常解决方案”的完整攻略,包含以下几部分内容: 问题描述 解决方案 示例说明 问题描述 使用 Spring Boot 时,如果要使用 Elasticsearch,一般会使用 Spring Data Elasticsearch(spring-boot-starter-data-elasticsearch),其中包含了 E…

    Java 2023年5月27日
    00
  • Java 下数据业务逻辑开发技术 JOOQ 和 SPL

    Java 下数据业务逻辑开发技术 JOOQ 和 SPL 的完整攻略 JOOQ(Java Object Oriented Querying)是一个 Java 版本的关系型数据库操作工具,它可以让用户使用 Java 对象和方法进行 SQL 查询和更新操作,JOOQ 可以解决 SQL 代码繁琐、难以维护、不能重用等问题。而 SPL(Stored Procedure…

    Java 2023年5月19日
    00
  • 解析Java中的Field类和Method类

    解析Java中的Field类和Method类攻略 什么是Field类和Method类 Field类和Method类都是Java反射的重要组成部分。Field类代表一个类或者接口的属性(成员变量),Method类代表一个类或者接口中的方法。 使用这两个类可以在运行时获取并操作类或接口中的属性和方法信息。 如何使用Field类 在Java中,每个类都有它的属性(…

    Java 2023年5月26日
    00
  • SpringBoot中整合MyBatis-Plus的方法示例

    Sure,下面是SpringBoot整合MyBatis-Plus的方法示例完整攻略: 一、前置准备 JDK 1.8+ Maven 3.0+ SpringBoot 2.0+ MyBatis-Plus 3.1.0+ 二、项目搭建 1. 创建SpringBoot项目 通过 Spring Initializer,创建一个 SpringBoot 项目,并导入 Mave…

    Java 2023年5月20日
    00
  • 为什么阿里巴巴要求日期格式化时必须有使用y表示年

    阿里巴巴规定日期格式化时必须使用y表示年,这是因为其涉及到两个重要的时间概念:年份和周数。 在时间处理过程中,使用两位数代表年份可能会存在歧义,例如2019年和1919年在只用两位数表示时是相同的。而使用四位数则可以清晰明确地表示年份,避免了可能发生的混淆和错误。因此,阿里巴巴强制使用四位数表示年份。 此外,阿里巴巴还要求在日期格式化时需要使用大写字母Y表示…

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