首页 > 其他分享 >什么是线程死锁

什么是线程死锁

时间:2023-03-28 13:13:51浏览次数:31  
标签:Thread 什么 死锁 线程 resource1 资源 resource2

死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程(线程)称为死锁进程(线程)。

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。

如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。

public class DeadLockDemo {
    private static Object resource1 = new Object();//资源 1
    private static Object resource2 = new Object();//资源 2

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (resource1) {
                System.out.println(Thread.currentThread() + "get resource1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "waiting get resource2");
                synchronized (resource2) {
                    System.out.println(Thread.currentThread() + "get resource2");
                }
            }
        }, "线程 1").start();

        new Thread(() -> {
            synchronized (resource2) {
                System.out.println(Thread.currentThread() + "get resource2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "waiting get resource1");
                synchronized (resource1) {
                    System.out.println(Thread.currentThread() + "get resource1");
                }
            }
        }, "线程 2").start();
    }
}

线程 A 通过 synchronized (resource1) 获得 resource1 的监视器锁,然后通过Thread.sleep(1000);让线程 A 休眠 1s 为的是让线程 B 得到CPU执行权,然后获取到 resource2 的监视器锁。线程 A 和线程 B 休眠结束了都开始企图请求获取对方的资源,然后这两个线程就会陷入互相等待的状态,这也就产生了死锁。上面的例子符合产生死锁的四个必要条件。

形成死锁的四个必要条件是什么

  • 互斥条件:线程(进程)对于所分配到的资源具有排它性,即一个资源只能被一个线程(进程)占用,直到被该线程(进程)释放
  • 请求与保持条件:一个线程(进程)因请求被占用资源而发生阻塞时,对已获得的资源保持不放。
  • 不剥夺条件:线程(进程)已获得的资源在末使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源。
  • 循环等待条件:当发生死锁时,所等待的线程(进程)必定会形成一个环路(类似于死循环),造成永久阻塞

如何避免线程死锁

我们只要破坏产生死锁的四个条件中的其中一个就可以了。

破坏互斥条件

这个条件我们没有办法破坏,因为我们用锁本来就是想让他们互斥的(临界资源需要互斥访问)。

破坏请求与保护条件

一次性申请所有的资源。

破坏不剥夺条件

占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。

破坏循环等待条件

靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。破坏循环等待条件。

我们对线程 2 的代码修改成下面这样就不会产生死锁了:

new Thread(() -> {
    synchronized (resource1) {
        System.out.println(Thread.currentThread() + "get resource1");
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread() + "waiting get resource2");
        synchronized (resource2) {
            System.out.println(Thread.currentThread() + "get resource2");
        }
    }
}, "线程 2").start();

线程 1 首先获得到 resource1 的监视器锁,这时候线程 2 就获取不到了。然后线程 1 再去获取 resource2 的监视器锁,可以获取到。然后线程 1 释放了对 resource1、resource2 的监视器锁的占用,线程 2 获取到就可以执行了。这样就破坏了破坏循环等待条件,因此避免了死锁。

 

标签:Thread,什么,死锁,线程,resource1,资源,resource2
From: https://www.cnblogs.com/xfeiyun/p/17264771.html

相关文章

  • 线程的 run()和 start()有什么区别?
    每个线程都是通过某个特定Thread对象所对应的方法run()来完成其操作的,run()方法称为线程体。通过调用Thread类的start()方法来启动一个线程。start()方法用于启动线程,run......
  • 为什么我们调用 start() 方法时会执行 run() 方法,为什么我们不能直接调用 run() 方法?
    new一个Thread,线程进入了新建状态。调用start()方法,会启动一个线程并使线程进入了就绪状态,当分配到时间片后就可以开始运行了。start()会执行线程的相应准备工作,然后......
  • 为什么 Go 不支持 []T 转换为 []interface
    为什么Go不支持[]T转换为[]interfaceGo语言中文网 2023-03-2708:52 发表于北京 以下文章来源于AlwaysBeta ,作者yongxinzAlwaysBeta.大厂程序员,专注......
  • 守护线程和用户线程有什么区别呢?
    在 Java中通常有两种线程:守护线程(DaemonThread)和用户线程(UserThread)。守护线程:是一种特殊的线程,在后台默默地完成一些系统性的服务,比如垃圾回收线程、JIT 线程都是......
  • 什么是上下文切换?
    多线程编程中一般线程的个数都大于CPU核心的个数,而一个CPU核心在任意时刻只能被一个线程使用,为了让这些线程都能得到有效执行,CPU采取的策略是为每个线程分配时间片并......
  • python apscheduler 定时任务的基本使用-8-线程执行器ThreadPoolExecutor
    pythonapscheduler定时任务的基本使用-8-线程执行器ThreadPoolExecutor1、线程执行器ThreadPoolExecutor先说个人总结假设启动线程数为N,任务数为M,misfire_grace_tim......
  • C#:多线程操作Dictionary
    为了在多线程环境下操作Dictionary,我们需要确保线程安全。其中一种实现方式是使用ConcurrentDictionary类,该类位于System.Collections.Concurrent命名空间中。Concurrent......
  • linux内核线程优先级配置
    linux内核线程优先级配置/*referencedriver/spi/spi.c*/#include<linux/sched/rt.h>#include<uapi/linux/sched/types.h>staticstructsched_paramparam={......
  • 什么是分而治之
       分而治之是一种常用的解决问题的方法,它将一个大问题分割成若干个规模较小且结构相同的子问题,然后通过递归的方式分别解决这些子问题,最后将这些子问题的解合并起来,......
  • 海外tiktok需要加速器吗?Tiktok加速器为什么一定要用?
    在全球范围内,海外tiktok是一款备受欢迎的社交媒体应用,已经拥有了数亿用户。然而,由于一些不可抗因素的限制,国内用户在访问海外tiktok时会遇到问题。这时,使用Tiktok加速器可......