Java零基础之多线程篇:讲解死锁和资源管理

news2024/9/24 17:18:58

哈喽,各位小伙伴们,你们好呀,我是喵手。运营社区:C站/掘金/腾讯云;欢迎大家常来逛逛

  今天我要给大家分享一些自己日常学习到的一些知识点,并以文字的形式跟大家一起交流,互相学习,一个人虽可以走的更快,但一群人可以走的更远。

  我是一名后端开发爱好者,工作日常接触到最多的就是Java语言啦,所以我都尽量抽业余时间把自己所学到所会的,通过文章的形式进行输出,希望以这种方式帮助到更多的初学者或者想入门的小伙伴们,同时也能对自己的技术进行沉淀,加以复盘,查缺补漏。

小伙伴们在批阅的过程中,如果觉得文章不错,欢迎点赞、收藏、关注哦。三连即是对作者我写作道路上最好的鼓励与支持!

前言

  在前几期中,我们基本已经对多线程有了一定的基础及实践。在多线程编程中,有一种情况,我们基本也能遇到,那就是死锁,对于死锁它也是一种常见的问题。当多个线程同时竞争资源时,如果它们互相等待对方释放资源,就会导致死锁的发生。本文将介绍死锁的概念以及如何在Java中进行资源管理来避免死锁。

摘要

  本文主要探讨了Java多线程中死锁的问题和资源管理的方法。首先对死锁的概念进行了解释,并介绍了死锁的必要条件。然后,通过实际的代码示例来展示死锁是如何发生的。接下来,介绍了几种常见的资源管理策略,包括避免死锁、检测死锁和解除死锁。最后,通过一个应用场景案例来说明如何使用资源管理策略来避免死锁的发生。

简介

  死锁是指两个或多个线程互相等待对方释放资源,导致程序无法继续执行的状态。死锁的发生是由于多个线程同时竞争资源,而资源的访问顺序不当引起的。在Java中,使用synchronized关键字可以实现对资源的互斥访问,但如果不正确地使用锁,就会导致死锁的发生。

源代码解析

以下是一个简单的死锁示例代码:

package com.example.javase.ms.threadDemo.day9;

/**
 * @Author ms
 * @Date 2024-04-13 19:51
 */
public class DeadlockDemo {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread1 acquired lock1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("Thread1 acquired lock2");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread2 acquired lock2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("Thread2 acquired lock1");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

  在上面的代码中,演示了一个典型的死锁情况。在Java中,死锁发生在两个或多个线程互相等待对方释放锁,而这些锁又被对方持有,导致无法继续执行的情况。

  在这个例子中,thread1首先获取lock1,然后等待1秒钟,之后尝试获取lock2。同样,thread2首先获取lock2,然后等待1秒钟,之后尝试获取lock1。如果thread1thread2几乎同时运行,它们可能会同时持有一个锁并等待另一个锁,这将导致死锁。

  代码执行的结果是,两个线程都会在尝试获取第二个锁的时候陷入等待状态,因为每个线程都在等待另一个线程释放它需要的锁。这将导致程序停止响应,因为没有线程能够继续执行。

  具体的输出可能如下所示,但实际的输出可能会因为线程调度的不确定性而有所不同:

Thread1 acquired lock1
Thread2 acquired lock2

  之后,两个线程都会在等待对方释放锁的地方停止,没有进一步的输出。

  为了避免死锁,其实可采取的措施有很多,比如:

  1. 锁排序:为所有的锁分配一个固定的顺序,并确保每个线程都按照这个顺序获取锁。
  2. 锁尝试:尝试获取锁,如果无法获取,则释放已持有的锁并稍后重试。
  3. 使用java.util.concurrent.locks包中的锁:这个包提供了一些可重入锁和条件同步,可以帮助避免死锁。

  在实际开发中,应该尽量避免死锁的发生,因为死锁会导致程序无法继续执行,而且可能很难诊断和解决。

应用场景案例

  假设现在有一个银行账户管理系统,每个账户都有一个账户号和余额。该系统允许多个线程同时对不同账户进行操作,但同一个账户不能同时被多个线程操作。为了避免死锁的发生,我们可以采用以下资源管理策略:

  1. 使用一个全局的锁对象来控制对账户的互斥访问。
  2. 在对账户进行操作之前,先检查该账户是否已经被其他线程锁定,如果是,则等待。
  3. 操作完毕后,释放锁定,并通知其他等待线程可以尝试获取锁。

  通过以上策略的应用,可以有效地避免死锁的发生。

优缺点分析

  资源管理策略的优点是可以避免死锁的发生,确保程序的正常执行。同时,它也能够提高多线程程序的性能,减少线程等待时间。

  然而,资源管理策略也存在一些缺点。首先,需要对每个涉及到资源互斥访问的地方进行修改,增加了代码的复杂性。其次,在高并发环境下,大量的线程等待锁的释放可能导致性能下降。

类代码方法介绍

  在上面的代码示例中,我们使用了synchronized关键字来实现资源的互斥访问。synchronized关键字可以应用于方法和代码块。

  • synchronized方法:将synchronized关键字应用于方法上,表示该方法是一个互斥方法,同一时间只能有一个线程执行该方法。
  • synchronized代码块:将synchronized关键字应用于代码块上,表示该代码块是一个互斥区域,同一时间只能有一个线程执行该代码块。

在实际开发中,根据不同的需求和性能要求,选择合适的方式来进行资源的互斥访问。

测试用例

  在上面的源代码解析中已经提供了一个死锁示例代码。这里我们本地来实际测试一下,通过debug模式,一步一步执行去体验,它究竟是如何死锁并且如何修改?

测试代码

package com.example.javase.ms.threadDemo.day9;

/**
 * @Author ms
 * @Date 2024-04-13 19:51
 */
public class DeadlockDemo {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (lock1) {
                System.out.println("Thread1 acquired lock1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock2) {
                    System.out.println("Thread1 acquired lock2");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock2) {
                System.out.println("Thread2 acquired lock2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lock1) {
                    System.out.println("Thread2 acquired lock1");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

  为了避免死锁的发生,你可以尝试修改示例代码中的锁的获取顺序,或者采用资源管理策略中的方法进行改进。

测试结果展示

  根据如上测试用例,这里我们本地执行一下,结果展示如下:

在这里插入图片描述

实际的输出与预期结果是一致的,具体可对应上截图。

Thread1 acquired lock1
Thread2 acquired lock2

测试代码分析

  根据如上代码作出解析,以便于同学们更好的理解,分析如下:该测试用例演示了一个死锁的情况。在main方法中,创建了两个线程thread1和thread2。线程thread1首先获取lock1锁,然后休眠1秒钟。接着,线程thread1尝试获取lock2锁。与此同时,线程thread2首先获取lock2锁,然后休眠1秒钟。接着,线程thread2尝试获取lock1锁。

  由于线程thread1占用了lock1锁并等待获取lock2锁,而线程thread2占用了lock2锁并等待获取lock1锁,这就造成了死锁。两个线程都在等待对方释放锁,导致程序无法继续执行。

  解决死锁问题的方法是合理地设计锁的获取顺序,避免出现循环等待的情况。

全文小结

  本文主要介绍了Java中死锁的问题和资源管理的方法。我们首先了解了死锁的概念和必要条件,然后通过实际的代码示例展示了死锁的发生。接下来,介绍了几种常见的资源管理策略,并通过一个应用场景案例说明了如何使用这些策略来避免死锁。最后,针对资源管理策略的优缺点进行了分析。

总结

  死锁呢,是多线程编程中常见的问题,合理的资源管理策略可以有效避免死锁的发生,我们也需要在开发中进行合理的设计及避免死锁。在Java中,可以使用synchronized关键字来实现资源的互斥访问。合理地使用锁对象,并避免资源的互相依赖性,可以最大程度地减少死锁的发生。因此,在多线程编程中,合理的资源管理策略非常重要。

… …

文末

好啦,以上就是我这期的全部内容,如果有任何疑问,欢迎下方留言哦,咱们下期见。

… …

学习不分先后,知识不分多少;事无巨细,当以虚心求教;三人行,必有我师焉!!!

wished for you successed !!!


⭐️若喜欢我,就请关注我叭。

⭐️若对您有用,就请点赞叭。

⭐️若有疑问,就请评论留言告诉我叭。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1989435.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Qt 跨平台支持任务栏进度条以及自绘消息通知显示

一.效果图 win效果图 mac效果图 二.测试demo地址: testwindowbar: 跨平台的任务栏进度条

语言模型-预训练模型(三)

预训练-BERT 传统方法与预训练方法的比较预训练方式 --BERT训练任务模型结构BERT结构-EmbeddingBERT结构-EncoderBERT结构-self-attention 传统方法与预训练方法的比较 思想解读: 预训练的概念就和我们人读书和工作一样;先是通过基础教育到大学毕业&…

Gradio之文字输入输出

Gradio 是一个简单直观的交互界面的SDK组件。 在python 环境中安装使用pip install gradio 然后运行以下代码,就能在浏览器中展示 # 输入name字符串,输出Hello {name}!字符串 def greet(name):return "Hello " name "!"demo gr.I…

Python统计单词列表

文章目录 需求分析demo 需求 给定一组字符串words和每行能显示的最大字符数max_length,计算需要多少行才能完整显示所有的单词。 分析 思路很简单,从给定的字符串中按空格分割单词列表,计算一行能显示的单词数,将其添加到list中…

全国产飞腾D2000/8 ITX主板+银河麒麟Kylin/UOS 操作系统,应用于工业一体机, 工业平板、加固IPAD

ITX-F601一体机主板产品规格书 产品概述 ITX-F601是一款高性能一体机主板,基于国产飞腾处理器D2000/8 核处理器设计研发,低海岸线IO 设计,可用于工业一体机、工业平板等,合理摆放CPU,MINI-PCIE,M.2 槽位&…

UKP3D9.3版本导出报表文件

UKP3D9.3(8.4)号安装包,支持输出节点属性的文本报表。这个功能的实用性很强,适用于批量修改数据。 命令:TXTREPORT

百元护眼台灯有什么推荐?松下、飞利浦、书客看哪个更胜一筹

当前,近视问题在人群中的蔓延趋势愈发显著,几乎成为一种普遍现象。据最新出炉的2024年度统计数据显示,我国儿童及青少年群体的近视率已飙升至触目惊心的52.7%,这一数字不仅凸显了近视问题的严峻态势,更在社会上激起了广…

Java常见面试题-06-web

文章目录 TCP 与 UDP 区别?什么是 HTTP 协议?TCP 的三次握手HTTP 中重定向和请求转发的区别?Get 和 Post 的区别?cookie 和 session 的区别?介绍一下Ajax以及其优势axios 是什么?怎样使用它?怎么…

ICMP(Ping)功能原理及其C++实现简介

ICMP(Ping)功能原理及其应用简介 一、 Ping功能简介 1、 原始套接字(Raw Socket) 原始套接字(‌Raw Socket)‌是一种特殊的网络编程接口,‌它允许直接接收和发送网络层的数据包,‌而不是通过传输层。‌这种套接字可以接收本机网…

推荐 3个实用且完全免费的在线工具,每天都会用到,无需登录打开即用

100font 100font是一个专业的免费商用字体下载网站,专注于收集、整理和分享各种免费无版权的商用字体。用户可以在这个平台上找到并下载简体中文、繁体中文、英文、日文、韩文等多种语言类型的字体。 该网站的特点包括清晰的分类和直观的下载流程,用户可…

金字塔原理帮助你理清思路,提升职场表达力

金字塔原理帮助你理清思路 1分钟理清思路,提升职场表达力 金字塔原理概述 原则 解释 模型 结论先行 中心思想和结论只有一个,放在最前面 SCQA模型 以上统下 任一层的思想必须是下一层次思想的概括 演绎推理 归纳推理 归类分组 每组中的思想必…

【Linux网络】其他协议和技术:DNS、ICMP、NAT

本篇博客补充了 TCP/IP 分层模型中,应用层的 DNS 协议、网络层的 ICMP 协议、网络层的 NAT 技术,旨在让读者更加深入理解网络协议栈的设计和网络编程。 目录 一、DNS 协议 1)技术背景 2)域名与域名解析 二、ICMP 协议 1&…

Webstorm的下载与安装

Webstorm的下载 1 在浏览器的地址栏输入https://www.jetbrains.com/webstorm/,进入主页面 2 点击右上角的Download按钮,进入下载页面,如图所示 Webstorm的安装 按步骤逐步安装即可

tomcat文件上传漏洞练习

1、靶场账号注册 vulfocus 注册后邮箱中点击激活 2、首页选择并开启靶场 复制映射的ip和端口 在浏览器输入ip和端口 改成put并把1.jsp中内容复制进去 3打开哥斯拉,连接上面的网址

TCP协议程序设计

文章目录 前言一、TCP协议程序是什么?二、使用步骤 1.服务器端与客户端2.实操展示总结 前言 TCP网络程序设计是指利用Socket类编写通信程序。利用TCP协议进行通讯的两个应用程序是有主次之分的,一个称为服务器程序,另一个称为客户机程序&…

Go语言编程大全,web微服务数据库十大专题精讲

本课程主要从数据结构、Go Module 依赖管理、IO编程、数据库编程、消息队列、加密技术与网络安全、爬虫与反爬虫、web开发、微服务通用技术、Kitex框架等方面讲解~ 链接:https://pan.quark.cn/s/d65337a0e60d

S32K144 CAN使用

S32K144是恩智浦半导体推出的一款高性能微控制器(MCU),主要针对汽车和高可靠性的工业应用。在汽车工业应用中使用CAN总线必不可少。 需要注意的是在ISO 11898(High Speed,速度在5kbps-1Mbps)中,隐形电平电压差在0附件…

收银机打印机相关知识 windows7 查看打印机名称--未来之窗智慧经营收银系统百科

一、名词解释 打印机型号,打印机厂家,打印机接口、打印机驱动,打印机名称 机器的机身都有,打印机品牌,型号 二、什么是打印机接口 2.1 usb接口 一般用在收银小票,结账单,水单 2.2 并口接口 …

RK3568平台开发系列讲解(文件系统篇)什么是 inode?

一、什么是 inode? 为了存储一个文件,除了本身的文件内容块,还需要一个地方来存储文件相关的元信息信息。承载这些文件元数据的数据结构就是 inode(index node),inode 包含了文件元信息,比如文件的所有者、权限、修改时间戳等。 可以使用 ls -i 查看一个文件的 inode 号…

java基础概念11-方法

一、什么是方法 方法(method)是程序中最小的执行单元。 方法中的程序,要不然就是一起执行,要不然就是一起不执行!!! 二、方法的定义 在Java中,方法定义的一般格式如下:…