多线程(十):总结

news2024/11/15 8:56:03

本章用来处理一下之前遗漏的很多问题,在多线程那一章,很多常见面试题都没有讲,这里再来补充一下。

HashTable, HashMap, ConcurrentHashMap 之间的区别

HashTable, HashMap, ConcurrentHashMap 都带有Map,它们其实都是 Map 的接口,都是以键值对的 形式来存储数据。

HashMap

HashMap是在JDK1.2中引入的Map的实现类。

HashMap是基于哈希表实现的,其主要的特点有:

  1. HashMap 的键值对均可以为 null(当key 为null 时,哈希会被赋值为0)
  2. 初始的size 默认为 16,每次以二倍的形式扩容,最大值为 2^30 
  3. 底层使用的数据结构为:数组 + 链表 + 红黑树
  4. 当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀;计算index方法:index = hash & (tab.length – 1)
  5.  HashMap 效率非常高,但线程不安全

HashTable

Hash table,叫做散列表(也叫哈希表),其主要特点有:

  1. 底层是由 数组 + 链表 实现的
  2. 无论是 key 还是 value 都是 不允许为 null 的
  3. 虽然线程是安全的,但是只是简单得用 synchronized 给所有方法加锁,相当于是对this加锁,也就是对整个HashTable对象进行加锁(非常无脑)                                      因为是 无脑加锁,所以Java官方并不推荐使用,而建议不涉及到线程安全问题时使用:HashMap,遇到线程安全问题时 使用:ConcurrentHashMap
  4. 实现线程安全的方式是在修改数据时锁住整个HashTable,所以效率非常低
  5. 初始size为11,扩容:newsize = olesize*2+1
    计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length【我查的】

ConcurrentHashMap

  1. 底层数据结构:数组 + 链表 + 红黑树
  2. ConcurrentHashMap 的键值不可以为null
  3. ConcurrentHashMap 最重要的点要说 线程安全

    ConcurrentHashMap 相比比较于HashTable 有很多的优化

核心思想就是降低 锁冲突的概率:

具体的优化手段有:

(1)锁粒度的控制

ConcurrentHashMap 不是锁整个对象,而是使用多把锁,对每个哈希桶(链表)都进行加锁,只有当两个线程同时访问同一个哈希桶时,才会产生锁冲突,这样也就降低了锁冲突的概率,性能也就提高了

(2) 只给读加锁,不给写加锁

我们知道 写会造成冲突,而只读不会有影响。

(3)充分利用到了CAS的特性

比如更新元素个数,都是通过CAS来实现的,而不是加锁

(4)ConcurrentHashMap 对于扩容操作,进行了特殊优化

HashTable的扩容是这样:当put元素的时候,发现当前的负载因子已经超过阀值了,就触发扩容。

扩容操作时这样:申请一个更大的数组,然后把这之前旧的数据给搬运到新的数组上

但这样的操作会存在这样的问题:如果元素个数特别多,那么搬运的操作就会开销很大

执行一个put操作,正常一个put会瞬间完成O(1)

但是触发扩容的这一下put,可能就会卡很久(正常情况下服务器都没问题,但也有极小概率会发生请求超时(put卡了,导致请求超时),虽然是极小概率,但是在大量数据下,就不是小问题了)

ConcurrentHashMap 在扩容时,就不再是直接一次性完成搬运了

而是搬运一点,具体是这样的

扩容过程中,旧的和新的会同时存在一段时间,每次进行哈希表的操作,都会把旧的内存上的元素搬运一部分到新的空间上,直到最终搬运完成,就释放旧的空间

在这个过程中如果要查询元素,旧的和新的一起查询;如果要插入元素,直接在新的上插入

;如果是要删除元素,那就直接删就可以了

具体的可以参考下面两篇博客:

 ConcurrentHashMap_亦安✘的博客-CSDN博客

死锁的成因, 和解决方案

什么是死锁

所谓死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。

产生死锁的三个经典案例

案例一:一个线程 一把锁

一个线程正常操作,不会发生线程安全问题,如果是说,针对一个线程,多次加锁,那么就会产生问题。

// 第一次加锁成功

lock();

// 再次尝试对其加锁,原来的锁还未被释放

lock();

// 加锁失败,造成阻塞等待

像这样,第二次加锁,再等待第一次加锁的资源释放,第一次加锁释放又在等待第二次加锁的完成,于是只能造成死锁。

对可重入锁和不可重入锁的补充

 如果同一个线程在重复获取同一把锁的过程中,形成了死锁。这把锁又被称为不可重入锁。而可重入锁的字面意思是“可以重新进入的锁”,即允许同一个线程多次获取同一把锁,不会出现死锁的情况。synchronized 是可重入锁

案例二:(两个线程,两把锁)

简单理解就是 车钥匙 在 家里 , 家钥匙 在 车里。

简单用伪代码来距离:

Object locker1 = new Object();
Object locker2 = new Object();


// 线程 T1
synchronized (locker1) {
    synchronized (locker2) {

    }
}


// 线程 T2
synchronized (locker2) {
    synchronized (locker1) {

    }
}

线程抢占式执行,假设 T1 和 T2 同时执行,T1拿到了 locker1 ,T2 拿到了locker2 ,都卡在了第一步,要想拿到 另一把锁,必须得让对方先释放,双方都无法释放,那么就造成了死锁。

案例三:(N个线程,M把锁)

哲学家吃面条问题

5位哲学家围着一张桌子,桌子上有几碗面条。这5位哲学家的左右手两边各有一根筷子(注意是一根,不是一双,两根筷子才是一双,才能拿来吃面,一根筷子无法吃面)

 5位哲学家相当于是5个线程,这些线程只有分别拿到左右手旁的两根筷子(各自要求的两把锁),才能完成进程,并释放自己所占用的锁。 

然后呢,在某一时刻,哲学家都想吃面条:他们同时拿起了自己右手边的那根筷子。5位哲学家、5根筷子,他们每个人都只拿了一根筷子(获取到了一个锁) 。于是他们每个人都完成不了各自的进程,也无法释放他们所占用的锁(筷子),都吃不到面条。

这又是一个死锁问题。

 解决办法

那么怎么解决呢?和上面死锁的解决方案相同——我们要分析为什么会出现死锁,就是因为线程对锁的互相等待,线程一要获取的锁被线程二占用着,但同时线程二要获取的锁又被线程一占用着,于是他们两个都无法获取到完整的锁,无法完成各自的进程,并释放锁。都处于一个循环等待的过程。

要解决死锁问题,重点就是解决循环等待问题。如果每个线程都按一定的顺序来获取对应的锁,比如在上面的栗子中,我们给5根筷子(5把锁)按从1到5的顺序进行编号,哲学家只能拿到到左右两边锁编号最小的那把锁。(已经拿到的锁不用进行编号的比较)

 形成死锁的四个条件

  1. 互斥性:当多个线程对同一把锁,有竞争。在某一时刻,最终只有一个线程可以拥有这把锁
  2. 不可抢夺性:当一个线程已经获取到了锁A,其他线程要想获取锁A,这个时候只能等该线程把A释放了之后再获取,不能中途抢夺别的线程的锁。
  3. 请求和保持性:当一个线程获取到了锁A,除非该线程自己释放锁A,否则该线程就一直保持占有锁A
  4. 循环等待性:在死锁中往往会出现,线程A等着线程B释放锁,同时线程B又在等着线程A来释放他所占有的锁,结果A、B的锁都无法正常释放,也都无法完成各自的进程,陷入了一个循环等待的状态

当上述四个条件某一条被破坏之后,死锁就解决了。

synchronized

synchronized的特点:

  1. 既是乐观锁,又是悲观锁
  2. 既是轻量级锁,又是重量级锁
  3. 轻量级锁基于自旋锁,重量级锁基于挂起等待锁
  4. 不是读写锁
  5. 是可重入锁
  6. 是非公平锁

synchronized关键字通常使用在下面四个地方:

  • synchronized修饰实例方法。

  • synchronized修饰静态方法。

  • synchronized修饰实例方法的代码块。

  • synchronized修饰静态方法的代码块。

锁升级

Java 1.6为了减少获得锁和释放锁带来的性能消耗,引入了“偏向锁”和“轻量级锁”,在Java SE 1.6中,锁一共有4种状态,级别从低到高依次是:无锁状态、偏向锁状态、轻量级锁状态和重量级锁状态,这几个状态会随着竞争情况逐渐升级。锁可以升级但不能降级,意味着偏向锁升级成轻量级锁后不能降级成偏向锁。这种锁升级却不能降级的策略,目的是为了提高获得锁和释放锁的效率。

如下图:

在这里插入图片描述

锁策略, cas 和 synchronized 优化过程

可以参考之前写过的文章:

 多线程(八):常见锁策略_我可是ikun啊的博客-CSDN博客

synchronized 和 ReentrantLock 之间的区别

相同点:

  1. synchronized 和 ReentrantLock 都是 Java 中提供的可重入锁

不同点:

  1. 用法不同:synchronized 可以用来修饰普通方法、静态方法和代码块;ReentrantLock 只能用于代码块;
  2. 获取和释放锁的机制不同:进入synchronized 块自动加锁和执行完后自动释放锁; ReentrantLock 需要显示的手动加锁和释放锁;
  3. 锁类型不同:synchronized 是非公平锁; ReentrantLock 默认为非公平锁,也可以手动指定为公平锁;
  4. 响应中断不同:synchronized 不能响应中断;ReentrantLock 可以响应中断,可用于解决死锁的问题;
  5. 底层实现不同:synchronized 是 JVM 层面通过监视器实现的;ReentrantLock 是基于 AQS 实现的。

线程池的执行流程和拒绝策略

线程池的执行流程:

  1. 当新加入一个任务时,先判断当前线程数是否大于核心线程数,如果结果为 false,则新建线程并执行任务;
  2. 如果结果为 true,则判断任务队列是否已满,如果结果为 false,则把任务添加到任务队列中等待线程执行
  3. 如果结果为 true,则判断当前线程数量是否超过最大线程数?如果结果为 false,则新建线程执行此任务
  4. 如果结果为 true,执行拒绝策略。

拒绝策略:

  1. AbortPolicy:中止策略,线程池会抛出异常并中止执行此任务;
  2. CallerRunsPolicy:把任务交给添加此任务的线程来执行;
  3. DiscardPolicy:忽略此任务(最新加入的任务);
  4. DiscardOldestPolicy:忽略最先加入队列的任务(最老的任务)。

线程池的执行流程图:

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

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

相关文章

数据库设计软件Power Designer详解教程(附源码)

版权声明 本文原创作者:谷哥的小弟作者博客地址:http://blog.csdn.net/lfdfhl Power Designer概述 Power Designer 是美国Sybase公司的CASE工具集,利用Power Designer可分别从概念数据模型(Conceptual Data Model)和物理数据模型(Physical D…

如何查看某个starter的详细官方文档

\qquad 相信很多小伙伴在用到一个没怎么接触过的starter的时候,也有不知道怎么使用,怎么配置的苦恼,本文介绍一种大部分开源技术都能使用的查看官方文档的方式。 1.首先在github搜索相关技术 \qquad 这里以redis为例,如下&#x…

QT学习之旅 - network连接

文章目录 网络知识点IP地址IPv4和IPv6 端口号(协议端口)端口分类UDP端口和TCP端口 networkpro文件.h文件.cpp文件 UDP连接绑定端口绑定成功后等待对方进行连接点击发送源码扩展: nodejs-udp服务端(用于跟QT程序进行通信)现象 网络知识点 IP地址 192.168.127.170(√) 192.168.…

结构化GPT用例,在CSDN私密社区中死磕@ada 探索SpringBoot

在CSDN私密社区中死磕ada 探索SpringBoot Q: Spring的核心概念是哪些?Q: Spring MVC的核心概念是哪些?Q: SpringBoot的核心概念有哪些?Q: 介绍下SpringBoot AutoConfiguration的机制。Q: SpringBootConfiguration 和 Configuration 的区别是&…

使用esp32+micropython+microdot搭建web(http+websocket)服务器(超详细)第二部分

使用esp32micropythonmicrodot搭建web(httpwebsocket)服务器(超详细)第二部分 microdot文档速查 什么是Microdot?Microdot是一个可以在micropython中搭建物联网web服务器的框架micropyton文档api速查 Quick reference for the ESP32 实现http服务器 …

基于cycle of curves的Nova证明系统

1. 引言 主要见斯坦福大学Wilson Nguyen、Dan Boneh和微软研究中心Srinath Setty 2023年论文《Revisiting the Nova Proof System on a Cycle of Curves》。 前序博客有: Nova: Recursive Zero-Knowledge Arguments from Folding Schemes学习笔记 在2021年Nova …

Java线程的六种状态(付代码解释)

目录 一.新建状态 (New) 解释 代码 运行结果 ​编辑 二.运行状态(Runnable) 解释 代码 运行结果 三.等待状态(Waiting) 解释 代码 运行结果 四.阻塞状态(Blocked) 解释 代码 运行结果 五.计时等待状态(…

PCB设计系列分享-开关稳压器接地处理

目录 概要 整体架构流程 技术名词解释 1.DCDC: 2.PGND: 3.AGND: 技术细节 1.认识1 2.认识2 3.综合 小结 概要 提示:这里可以添加技术概要 如何使用带有模拟接地层(AGND)和功率接地层(PGND)的开关稳压器? 这是许多开发人员在设计…

启用Windows应急重启功能

博主最近发现了Windows隐藏功能——应急重启,并且这个功能可以追溯到Windows Vista!但是因为大家习惯长按电源键关机所以就鲜为人知。今天博主叫你如何使用应急重启功能。 因为使用功能都无法截图,所以就不展示图片了。 第一步,按住CtrlAltD…

什么是元宇宙?元宇宙由哪些关键技术、设备构成?

元宇宙近几年来火爆起来,各个行业争先恐后加入。从目前来看,元宇宙初步体现在游戏娱乐行业、社交、消费、数字孪生等方面。元宇宙近两年开始在各个行业快速崛起,但各个行业并没有一个清晰的发展方向,那么什么是元宇宙? 元宇宙到底由哪些技术和设备组成?查询了很多资…

用于SOLIDWORKS装配体的X光机——SOLIDWORKS装配体直观工具

​ SOLIDWORKS报告和故障排除的瑞士军刀 如何快速的根据条件会装配体中的零部件进行分类? 如何快速找到装配体中的某些特定零件? 如何快速在图形区域中突出显示出特定的零部件? 如果你用过“SOLIDWORKS装配体直观工具”的话,…

JMeter之常见逻辑控制器实践

ForEach Controller(循环控制器) 组件使用说明 选项说明: ①需要循环遍历名称(name); ②循环变量的下标起点(name_0); ③循环变量的下标终点(name_4); ④引用变量名称定义; ⑤在变量后加_&…

jsvmp xs逆向学习

内容仅供参考学习 欢迎朋友们V一起交流: zcxl7_7 首先直接搜索关键词 找到encrypt位置 接下来就是分析encrypt过程,详情请看专栏中的文章

leetcode 2448. Minimum Cost to Make Array Equal(使数组相等的最小成本)

数组nums的元素每次可进行下面的操作: 每个元素1 或者 -1。 操作一次的cost是cost[i]. 问把nums的元素全部变为相等的元素需要的最小的cost. 思路: nums的元素全部变为相等的元素,这个相等的元素是多少,现在不知道。 一旦知道了…

Jmeter查看结果树之查看响应的13种方法

目录 前言: 1、Text 2、RegExpTester 3、BoundaryExtractorTester 4、CSSSelectorTester 5、XpathTester 6、JSONPathTester 7、HTML 8、HTMLSourceFormatted 9、HTML(downloadresources) 10、Document 11、JSON 12、XML 13、B…

RPA×IDP×AIGC,实在智能打造全新“超进化”文档审阅超自动化解决方案

企业商业活动频繁,每日都有大量文档被创建、书写、传递,需要人工审阅核查,以确保其准确性和合法性。这是对企业文档管理的一个巨大挑战,尤其对于金融机构、审计机构等文本相关岗位的工作人员来说更是如此。传统的文档审核通常需要…

集合和泛型的详细讲解

集合 1)可以动态保存任意多个对象,使用比较方便! 2)提供了一系列方便的操作对象的方法:add、remove、set、get等3)使用集合添加,删除新元素的示意代码-简洁了 集合的框架体系 …

实时检测Aruco标签坐标opencv-python之添加卡尔曼滤波

在实时检测Aruco标签坐标用于定位的时候发现,追踪效果不是很好,于是在检测过程中添加了卡尔曼滤波,在aruco检测算法检测不到aruco标签的时候,调用卡尔曼滤波算法(KalmanFilter),补偿丢失的定位的…

Rabbitmq学习

文章目录 前言RabbitMQ 1 同步调用和异步调用2 常见的MQ对比3 安装RabbitMQ4 RabbitMQ学习4.1 helloworld学习 5 Spring AMQP5.1 AMQP的入门案例(使用rabbittemplate进行消息发送和接受)5.2 RabbitMQ的workquene5.3 发布订阅模型(exchange(广播fanout 路由direct 话题topic))5.…

RK3588 修改USB/Sata/TF挂载点

文章目录 概要整体架构流程技术名词解释技术细节小结APP概要 rk3588 android12 平台的挂载点是:/storage/设备卷名(即uuid) 对上层开发不太友好,因此需要固定某个挂载点提供上层app调用。 修改后的路径效果如下: 整体架构流程 从概要图示中可知:对每个挂载点创建软连接来…