死锁产生的4个必要条件

本文详细解释了Java中死锁的概念,探讨了其四个必要条件,并通过示例展示了如何形成和避免死锁,包括按顺序获取锁、使用tryLock()、避免嵌套锁等策略。
摘要由CSDN通过智能技术生成

在Java中,死锁是一种多线程并发执行的情况,其中两个或多个线程相互等待对方释放持有的资源,导致所有线程无法继续执行。这种情况下,系统处于僵持状态,被称为死锁。

死锁产生的条件通常是由于每个线程都在等待另一个线程释放资源,而这些资源又是其他线程正在等待的。死锁的发生通常涉及到多个锁、多个线程以及竞争共享资源。

互斥条件(Mutual Exclusion)

至少有一个资源必须处于非共享模式,即一次只能被一个进程使用。如果一个进程占用了资源,其他进程必须等待释放。

示例代码:

public class MutualExclusionExample {
    private static class Resource {
    }

    private static Resource resourceA = new Resource();
    private static Resource resourceB = new Resource();

    public static void main(String[] args) {
        Thread threadA = new Thread(() -> {
            synchronized (resourceA) {
                System.out.println("Thread A: Holding resource A");
                // 模拟一些工作
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (resourceB) {
                    System.out.println("Thread A: Holding resource B");
                }
            }
        });

        Thread threadB = new Thread(() -> {
            synchronized (resourceB) {
                System.out.println("Thread B: Holding resource B");
                // 模拟一些工作
                try {
                    Thread.sleep(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (resourceA) {
                    System.out.println("Thread B: Holding resource A");
                }
            }
        });

        threadA.start();
        threadB.start();
    }
}

请求与保持条件(Hold and Wait)

进程已经保持至少一个资源,并且正在请求另一个资源,但由于其他进程持有了被请求的资源,因此该进程会被阻塞。进程在等待其他资源的同时,不释放已经持有的资源。

示例代码:

public class HoldAndWaitExample {
    private static class HoldAndWaitResource {
        synchronized void methodA(HoldAndWaitResource other) {
            System.out.println(Thread.currentThread().getName() + " acquiring resource A");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            other.methodB(this);
            System.out.println(Thread.currentThread().getName() + " releasing resource A");
        }

        synchronized void methodB(HoldAndWaitResource other) {
            System.out.println(Thread.currentThread().getName() + " acquiring resource B");
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            other.methodA(this);
            System.out.println(Thread.currentThread().getName() + " releasing resource B");
        }
    }

    public static void main(String[] args) {
        HoldAndWaitResource resourceA = new HoldAndWaitResource();
        HoldAndWaitResource resourceB = new HoldAndWaitResource();

        Thread threadA = new Thread(() -> resourceA.methodA(resourceB), "Thread A");
        Thread threadB = new Thread(() -> resourceB.methodB(resourceA), "Thread B");

        threadA.start();
        threadB.start();
    }
}

非剥夺条件(No Preemption)

系统不能抢占进程已经占有的资源,只能在该进程自愿释放资源时,才能够重新分配资源给其他进程。

示例代码:

public class NoPreemptionExample {
    private static class NoPreemptionResource {
        private boolean locked = false;

        synchronized void lock() {
            while (locked) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            locked = true;
        }

        synchronized void unlock() {
            locked = false;
            notify();
        }
    }

    public static void main(String[] args) {
        NoPreemptionResource resource = new NoPreemptionResource();

        Thread threadA = new Thread(() -> {
            resource.lock();
            System.out.println("Thread A: Holding the resource");
            // 模拟一些工作
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.unlock();
            System.out.println("Thread A: Releasing the resource");
        });

        Thread threadB = new Thread(() -> {
            resource.lock();
            System.out.println("Thread B: Holding the resource");
            // 模拟一些工作
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.unlock();
            System.out.println("Thread B: Releasing the resource");
        });

        threadA.start();
        threadB.start();
    }
}

循环等待条件(Circular Wait)

存在一个等待链,使得每个进程都占用着下一个进程所需的资源。形成一个环路,导致系统无法向前推进。

示例代码:

public class CircularWaitExample {
    private static class CircularWaitResource {
        private static final Object lockA = new Object();
        private static final Object lockB = new Object();

        public void methodA() {
            synchronized (lockA) {
                System.out.println(Thread.currentThread().getName() + " acquired lockA");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                methodB();
            }
        }

        public void methodB() {
            synchronized (lockB) {
                System.out.println(Thread.currentThread().getName() + " acquired lockB");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                methodA();
            }
        }
    }

    public static void main(String[] args) {
        CircularWaitResource resource = new CircularWaitResource();

        Thread threadA = new Thread(() -> resource.methodA(), "Thread A");
        Thread threadB = new Thread(() -> resource.methodB(), "Thread B");

        threadA.start();
        threadB.start();
    }

避免死锁

  1. 按顺序获取锁: 规定所有线程按照相同的顺序获取锁。这样可以降低死锁的可能性,因为线程按照相同的顺序请求资源,不会形成循环等待条件。

  2. 使用定时锁: 在获取锁时设置超时时间,如果在规定时间内没有获取到锁,则放弃当前锁并释放已获得的锁。这有助于防止持有锁的线程在等待其他资源时导致死锁。

  3. 使用 tryLock(): 在Java中,ReentrantLock类提供了tryLock()方法,该方法尝试获取锁,但不会阻塞线程。通过使用tryLock(),可以避免线程在等待资源时阻塞。

  4. 避免嵌套锁: 尽量避免在持有一个锁的时候去请求另一个锁。如果需要多个锁,可以尝试设计成获取一个锁的时候释放其他锁,以减少持有锁的时间。

  5. 使用统一的锁顺序: 如果多个线程需要获取多个锁,确保所有线程以相同的顺序获取这些锁。这样可以减少循环等待的可能性。

  6. 使用锁的层次结构: 将锁划分为层次结构,按照层级顺序获取锁。例如,按照资源的级别从低到高获取锁,而不是随意获取。

  7. 使用事务: 在数据库操作中,使用事务来确保一组操作的一致性,可以减少死锁的可能性。数据库管理系统通常具有处理并发事务的机制。

  8. 使用专门的工具和算法: 一些系统和工具提供死锁检测和解决的机制。使用这些工具可以及时发现死锁并采取适当的措施。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值