深入解析哈希表、哈希映射和并发哈希映射的区别,以及死锁的成因和解决方案

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 深入解析哈希表、哈希映射和并发哈希映射的区别,以及死锁的成因和解决方案

死锁

死锁是多线程编程中常见的问题,当两个或多个线程互相等待对方持有的资源而无法继续执行时,就会发生死锁。这种情况下,程序会陷入无法恢复的状态,造成程序停滞或崩溃。以下是死锁产生的常见原因和解决方案。

死锁产生条件

  1. 互斥访问资源:多个线程相互竞争访问资源,如果资源被一个线程持有,其他线程无法获取到该资源。
  2. 不可抢占:资源请求者不能强制从资源占有者手中夺取资源,资源只能由资源占有者主动释放。
  3. 循环等待:多个线程形成环形的资源依赖关系,每个线程都在等待下一个线程释放资源。
  4. 占用并等待:线程在持有一个资源的同时,又请求其他资源,导致其他线程无法继续执行。
    当上述四个条件都成立时,死锁变形成了。

可以举例“哲学家就餐问题”,有一群哲学家围着一张桌子吃饭,每两个哲学家之间放一个筷子,哲学家只做两件事:思考人生 或者 吃面条。 思考人生的时候就会放下筷子。吃面条就会拿起左右两边的筷子(先拿起左边, 再拿起右边),哲学家发现筷子拿不起来就会阻塞等待思考人生。五个哲学家同一时刻同时拿起左的筷子,再去拿右边的筷子就会发现筷子已被占有,就会阻塞等待,进行思考人生,哲学家们互相挂起等待就会形成“死锁”。

解决方案

如何解决死锁呢,那就是打破死锁形成的四个必要条件。

  • 破坏互斥条件:对于一些非必要的资源,可以改为共享资源,多个线程可以同时访问,从而避免互斥。
  • 破坏占用并等待:当线程需要多个资源时,一次性获取所需资源,而不是一个个依次获取,或者采用资源预分配的策略。
  • 破坏循环等待:通过对资源进行编号或者按照固定的顺序申请资源,避免形成循环等待。

其中最容易破坏的就是 “循环等待”

破坏循环等待最常用的一种死锁阻止技术就是锁排序。假设有 N 个线程尝试获取 M 把锁,就可以针对 M 把锁进行编号。N 个线程尝试获取锁的时候,都按照固定的按编号由小到大顺序来获取锁。这样就可以避免环路等待。

下面分别演示会产生环路等待的代码与不会产生环路等待的代码。

可能会产生环路等待:

public static void main(String[] args) {
        Object lock1 = new Object();
        Object lock2 = new Object();
        Thread t1 = new Thread(){
            @Override
            public void run() {
                //获取锁
                synchronized (lock1){
                    synchronized (lock2){
                    }
                }
            }
        };
        Thread t2 = new Thread(){
            @Override
            public void run() {
                synchronized (lock2){
                    synchronized (lock1){
                    }
                }
            }
        };
        t1.start();
        t2.start();
    }

不会产生环路等待的代码:

public static void main(String[] args) {
        Object lock1 = new Object();
        Object lock2 = new Object();
        Thread t1 = new Thread(){
            @Override
            public void run() {
                //获取锁
                synchronized (lock1){
                    synchronized (lock2){
                    }
                }
            }
        };
        Thread t2 = new Thread(){
            @Override
            public void run() {
                synchronized (lock1){
                    synchronized (lock2){
                    }
                }
            }
        };
        t1.start();
        t2.start();
    }

约定好顺序,先获取lock1锁再获取lock2锁

HashTable

HashTable只是对关键方法加上了 synchronized 关键字。

这就相当于对hashtable对象直接进行加锁。

  • 多个线程访问同一个hashtable就会发送锁冲突
  • 由于同步锁的存在,哈希表的性能相对较差。在高并发情况下,多线程的竞争可能会导致性能下降。
  • 哈希表不允许键和值为 null。

ConcurrentHashMap

  • 每个链表的头结点作为锁对象,降低了锁冲突概率
  • 读操作没有加锁,写操作进行了加锁依旧使用synchronized
  • 充分利用 CAS 特性
  • 优化了扩容方式
  1. 化整为零,当发现需要扩容时,会创建一个新的数组,并进行一部分的数据搬运
  2. 插入新元素时就会直接给新表里插入元素,并搬运一部分数据
  3. 删除元素时,新旧表都会进行查找,元素在那个表上,在那个表上删除
  4. 直到旧表元素搬运完成,才会把旧表进行删除

只有当两个线程访问同一个哈希桶上的数据才会进行锁冲突。

HashMap

  • 线程不安全:哈希映射是非线程安全的,不适用于多线程环境。在多线程环境下使用时,可能导致数据不一致的问题。
  • 性能:哈希映射的性能较好,由于没有同步锁的开销,能够更快地执行插入、查找和删除操作。


相关文章
|
17天前
|
存储 算法 安全
基于 Go 语言的公司内网管理软件哈希表算法深度解析与研究
在数字化办公中,公司内网管理软件通过哈希表算法保障信息安全与高效管理。哈希表基于键值对存储和查找,如用户登录验证、设备信息管理和文件权限控制等场景,Go语言实现的哈希表能快速验证用户信息,提升管理效率,确保网络稳定运行。
26 0
|
2月前
|
Kubernetes Linux 虚拟化
入门级容器技术解析:Docker和K8s的区别与关系
本文介绍了容器技术的发展历程及其重要组成部分Docker和Kubernetes。从传统物理机到虚拟机,再到容器化,每一步都旨在更高效地利用服务器资源并简化应用部署。容器技术通过隔离环境、减少依赖冲突和提高可移植性,解决了传统部署方式中的诸多问题。Docker作为容器化平台,专注于创建和管理容器;而Kubernetes则是一个强大的容器编排系统,用于自动化部署、扩展和管理容器化应用。两者相辅相成,共同推动了现代云原生应用的快速发展。
328 11
|
2月前
|
文字识别 开发者 数据处理
多模态数据信息提取解决方案评测报告!
阿里云推出的《多模态数据信息提取》解决方案,利用AI技术从文本、图像、音频和视频中提取关键信息,支持多种应用场景,大幅提升数据处理效率。评测涵盖部署体验、文档清晰度、模板简化、示例验证及需求适配性等方面。方案表现出色,部署简单直观,功能强大,适合多种业务场景。建议增加交互提示、多语言支持及优化OCR和音频转写功能...
135 3
多模态数据信息提取解决方案评测报告!
|
2月前
|
Serverless 对象存储 人工智能
智能文件解析:体验阿里云多模态信息提取解决方案
在当今数据驱动的时代,信息的获取和处理效率直接影响着企业决策的速度和质量。然而,面对日益多样化的文件格式(文本、图像、音频、视频),传统的处理方法显然已经无法满足需求。
115 4
智能文件解析:体验阿里云多模态信息提取解决方案
|
3月前
|
存储 监控 算法
企业内网监控系统中基于哈希表的 C# 算法解析
在企业内网监控系统中,哈希表作为一种高效的数据结构,能够快速处理大量网络连接和用户操作记录,确保网络安全与效率。通过C#代码示例展示了如何使用哈希表存储和管理用户的登录时间、访问IP及操作行为等信息,实现快速的查找、插入和删除操作。哈希表的应用显著提升了系统的实时性和准确性,尽管存在哈希冲突等问题,但通过合理设计哈希函数和冲突解决策略,可以确保系统稳定运行,为企业提供有力的安全保障。
|
4月前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
299 2
|
4月前
|
Java 编译器 API
深入解析:JDK与JVM的区别及联系
在Java开发和运行环境中,JDK(Java Development Kit)和JVM(Java Virtual Machine)是两个核心概念,它们在Java程序的开发、编译和运行过程中扮演着不同的角色。本文将深入解析JDK与JVM的区别及其内在联系,为Java开发者提供清晰的技术干货。
72 1
|
4月前
|
域名解析 网络协议 安全
反向DNS解析是从IP地址到域名的映射,主要作用于验证和识别,提高通信来源的可信度和可追溯性
在网络世界中,反向DNS解析是从IP地址到域名的映射,主要作用于验证和识别,提高通信来源的可信度和可追溯性。它在邮件服务器验证、网络安全等领域至关重要,帮助识别恶意行为,增强网络安全性。尽管存在配置错误等挑战,但正确管理下,反向DNS解析能显著提升网络环境的安全性和可靠性。
282 3
|
4月前
|
监控 网络协议 算法
OSPFv2与OSPFv3的区别:全面解析与应用场景
OSPFv2与OSPFv3的区别:全面解析与应用场景
109 0
|
4月前
|
存储 安全 Java
Java多线程编程中的并发容器:深入解析与实战应用####
在本文中,我们将探讨Java多线程编程中的一个核心话题——并发容器。不同于传统单一线程环境下的数据结构,并发容器专为多线程场景设计,确保数据访问的线程安全性和高效性。我们将从基础概念出发,逐步深入到`java.util.concurrent`包下的核心并发容器实现,如`ConcurrentHashMap`、`CopyOnWriteArrayList`以及`BlockingQueue`等,通过实例代码演示其使用方法,并分析它们背后的设计原理与适用场景。无论你是Java并发编程的初学者还是希望深化理解的开发者,本文都将为你提供有价值的见解与实践指导。 --- ####

热门文章

最新文章

推荐镜像

更多