多线程之死锁

简介: 多线程之死锁

死锁是在我们的程序中出现了程序的嵌套锁导致的

死锁是一个错误

是多线程中很重要的一共知识点

示意图

B等着A释放锁

A等着B释放锁

介绍线程

线程是操作系统能够进行运算调度的最小单位,他被包含在进程之内,是进程中实际运作的单位

每一个线程都有独立的代码和数据空间

线程在执行代码的时候

线程在执行的时候 具有随机性

CPU的执行权随时有可能被其他的线程抢走

线程里有一个概念叫锁

当有线程进入时 锁会自动关闭

线程出去后锁会自动打开

如果有线程先拿到锁的对象 获取了CPU的执行权

其他的线程就算拿到CPU的执行权也会被关在外面

其他线程抢到CPU执行权后会发现无法获得锁的对象

此时锁已经关闭

死锁就是线程1,2各自拿到了锁A,B的对象

线程1,2得获取锁B,A的对象才能进入下一步

但是需要的锁对象被对方已经拿到了

代码实现

public class MyThread extends Thread{
    //创建锁的对象
    static Object objA=new Object();
    static Object objB=new Object();
 
    @Override
    public void run() {
        if("线程A".equals(getName())){
            synchronized (objA){
                System.out.println("线程A拿到了A锁");
                synchronized (objB){
                    System.out.println("线程A拿到B锁,执行完毕");
                }
            }
        }
        else if("线程B".equals(getName())){
            synchronized (objB){
                System.out.println("线程B拿到了B锁");
                synchronized (objA){
                    System.out.println("线程B拿到了A锁,执行完毕");
                }
            }
        }
    }
}
public class Main {
    public static void main(String[] args) {
 
        MyThread t1 = new MyThread();
        MyThread t2 = new MyThread();
 
        t1.setName("线程A");
        t2.setName("线程B");
 
        t1.start();
        t2.start();
    }
}

控制台显示

程序未停止运行

避免死锁的方法

  1. 加锁顺序: 确定所有线程获取锁的顺序,然后要求所有线程都按照相同的顺序获取锁。
  2. 超时机制: 确定一个超时时间,在尝试获取锁之后,如果超过一定时间还未能成功获取锁,则放弃当前的锁并释放已经获取的锁。
  3. 资源分配策略: 采用预防性分配资源策略,以最大限度地减少发生死锁的可能性。
  4. 死锁检测: 建立一个机制来检测系统中是否存在死锁,一旦检测到死锁,就采取相应的措施来解除死锁。
  5. 避免环路等待: 确保不存在循环等待的情况,例如通过给资源编号,要求线程必须按照递增的顺序请求资源。

银行家算法

一种操作系统基本算法

有人去银行借钱 应该说出借的钱的数量

而银行会根据借的数量 结合自己银行所拥有的财产放出借的钱

银行会在自己的能里范围内最大满足顾客的需求

现在有进程要进入系统 抢占CPU 系统会分析给予资源分配 否则让其等

银行家算法基本原理如下:

  1. 系统在运行时会为每个进程和资源分配一个最大需求量和当前已分配量。
  2. 当一个进程请求资源时,系统会先检查是否有足够的资源可以分配给该进程,如果有,则会尝试分配资源给该进程并检查是否会导致系统进入不安全状态。
  3. 如果分配资源给该进程不会导致系统进入不安全状态,那么就会分配资源给该进程;否则,系统会拒绝分配资源,直到系统处于安全状态。

通过使用银行家算法,系统可以有效地避免死锁情况的发生,保证系统资源的合理利用。

在这段代码中,首先用户需要输入进程数和资源数,然后输入最大需求矩阵、已分配矩阵和可用资源向量。接着程序会根据用户输入计算出需求矩阵。然后通过银行家算法的安全性检查,判断系统是否处于安全状态。

在 isSafe 方法中,程序会模拟系统对进程的资源请求和释放,并通过检查每次操作后系统是否仍然处于安全状态来判断系统整体是否处于安全状态。如果系统可以满足所有进程的资源请求并且不会发生死锁,则系统被认为是处于安全状态。

最后,在 main 方法中调用 isSafe 方法,根据返回的结果输出相应的信息,告知用户系统当前的安全状态。

import java.util.Scanner;
 
public class BankersAlgorithm {
 
    private int[][] maximum; // 最大需求矩阵
    private int[][] allocation; // 已分配矩阵
    private int[][] need; // 需求矩阵
    private int[] available; // 可用资源向量
    private int numberOfProcesses; // 进程数
    private int numberOfResources; // 资源数
 
    // 初始化资源信息
    public void initializeData() {
        Scanner scanner = new Scanner(System.in);
        
        System.out.println("Enter number of processes:");
        numberOfProcesses = scanner.nextInt();
        
        System.out.println("Enter number of resources:");
        numberOfResources = scanner.nextInt();
        
        maximum = new int[numberOfProcesses][numberOfResources];
        allocation = new int[numberOfProcesses][numberOfResources];
        need = new int[numberOfProcesses][numberOfResources];
        available = new int[numberOfResources];
 
        // 输入最大需求矩阵
        System.out.println("Enter maximum matrix:");
        for (int i = 0; i < numberOfProcesses; i++) {
            for (int j = 0; j < numberOfResources; j++) {
                maximum[i][j] = scanner.nextInt();
            }
        }
 
        // 输入已分配矩阵
        System.out.println("Enter allocation matrix:");
        for (int i = 0; i < numberOfProcesses; i++) {
            for (int j = 0; j < numberOfResources; j++) {
                allocation[i][j] = scanner.nextInt();
                // 计算需求矩阵
                need[i][j] = maximum[i][j] - allocation[i][j];
            }
        }
 
        // 输入可用资源向量
        System.out.println("Enter available vector:");
        for (int i = 0; i < numberOfResources; i++) {
            available[i] = scanner.nextInt();
        }
        
        scanner.close();
    }
 
    // 银行家算法检查系统是否处于安全状态
    public boolean isSafe() {
        boolean[] finish = new boolean[numberOfProcesses];
        int[] work = new int[numberOfResources];
        
        // 初始化 work 数组
        for (int i = 0; i < numberOfResources; i++) {
            work[i] = available[i];
        }
 
        // 初始化 finish 数组
        for (int i = 0; i < numberOfProcesses; i++) {
            finish[i] = false;
        }
 
        int count = 0;
        while (count < numberOfProcesses) {
            boolean found = false;
            for (int i = 0; i < numberOfProcesses; i++) {
                if (!finish[i]) {
                    int j;
                    for (j = 0; j < numberOfResources; j++) {
                        if (need[i][j] > work[j]) {
                            break;
                        }
                    }
                    if (j == numberOfResources) {
                        // 如果进程可以被满足
                        for (int k = 0; k < numberOfResources; k++) {
                            work[k] += allocation[i][k];
                        }
                        finish[i] = true;
                        found = true;
                        count++;
                    }
                }
            }
            if (!found) {
                return false; // 无法满足某个进程的需求
            }
        }
        return true; // 所有进程均可以满足
    }
 
    public static void main(String[] args) {
        BankersAlgorithm bankersAlgorithm = new BankersAlgorithm();
        bankersAlgorithm.initializeData();
        if (bankersAlgorithm.isSafe()) {
            System.out.println("System is in safe state.");
        } else {
            System.out.println("System is in unsafe state.");
        }
    }
}


目录
相关文章
|
2月前
|
监控 Linux 编译器
多线程死锁检测的分析与实现(linux c)-有向图的应用
在日常的软件开发中,多线程是不可避免的,使用多线程中的一大问题就是线程对锁的不合理使用造成的死锁,死锁一旦发生,将导致多线程程序响应时间长,吞吐量下降甚至宕机崩溃,那么如何检测出一个多线程程序中是否存在死锁呢?在提出解决方案之前,先对死锁产生的原因以及产生的现象做一个分析。最后在用有向环来检测多线程中是否存在死锁的问题。
76 0
|
2月前
|
存储 安全 Java
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
并发编程知识点(volatile、JMM、锁、CAS、阻塞队列、线程池、死锁)
81 3
|
19天前
|
Arthas 监控 Java
深入解析与解决高并发下的线程池死锁问题
在高并发的互联网应用中,遇到线程池死锁问题导致响应延迟和超时。问题源于库存服务的悲观锁策略和线程池配置不当。通过以下方式解决:1) 采用乐观锁(如Spring Data JPA的@Version注解)替换悲观锁,减少线程等待;2) 动态调整线程池参数,如核心线程数、最大线程数和拒绝策略,以适应业务负载变化;3) 实施超时和重试机制,减少资源占用。这些改进提高了系统稳定性和用户体验。
44 2
|
19天前
|
Java
在Java中,死锁是指两个或多个线程互相等待对方释放资源,从而导致所有线程都无法继续执行的情况。
【6月更文挑战第24天】在Java并发中,死锁是多线程互相等待资源导致的僵局。避免死锁的关键策略包括:防止锁嵌套,设定固定的加锁顺序,使用`tryLock`带超时,避免无限等待,减少锁的持有时间,利用高级同步工具如`java.util.concurrent`,以及实施死锁检测和恢复机制。通过这些方法,可以提升程序的并发安全性。
15 1
|
23天前
|
Java
死锁是线程间争夺资源造成的无限等待现象,Java示例展示了两个线程各自持有资源并等待对方释放,导致死锁。`
【6月更文挑战第20天】死锁是线程间争夺资源造成的无限等待现象,Java示例展示了两个线程各自持有资源并等待对方释放,导致死锁。`volatile`保证变量的可见性和部分原子性,确保多线程环境中值的即时更新。与`synchronized`相比,`volatile`作用于单个变量,不保证原子操作,同步范围有限,但开销较小。`synchronized`提供更全面的内存语义,保证原子性和可见性,适用于复杂并发控制。
19 3
|
1月前
|
Python
Python多线程中递归锁如何解决死锁问题的详细阐述
Python多线程中递归锁如何解决死锁问题的详细阐述
|
1月前
|
安全 Python
Python多线程中的死锁与递归锁
Python多线程中的死锁与递归锁
|
17天前
|
SQL 安全 Java
JUC多线程-线程池-Thredalocal-CAS-AQS-死锁
JUC多线程-线程池-Thredalocal-CAS-AQS-死锁
|
18天前
|
Java
java线程之死锁
java线程之死锁
11 0
|
19天前
|
Java
synchronized关键字在Java中为多线程编程提供了一种简便的方式来管理并发,防止数据竞争和死锁等问题
Java的`synchronized`关键字确保多线程环境中的数据一致性,通过锁定代码段或方法防止并发冲突。它可修饰方法(整个方法为临界区)或代码块(指定对象锁)。例如,同步方法只允许一个线程执行,同步代码块则更灵活,可锁定特定对象。使用时需谨慎,以避免性能影响和死锁。
14 0