面试问答总结之并发编程

文章目录

  • 🐒个人主页:信计2102罗铠威
  • 🏅JavaEE系列专栏
    • 📖前言:
    • 🎀多线程的优点、缺点
    • 🐕并发编程的核心问题 :不可见性、乱序性、非原子性
      • 🪀不可见性
      • 🪀乱序性
      • 🪀非原子性
      • 🧸JMM(java内存模型)
      • 🏅volatile关键字:保证可见性、禁止指令重排序
      • 🐕CAS机制 (Conpare And Swap 比较并交换)
      • 🐕CAS会产生ABA问题
    • 🏨java中锁的分类
      • 🪀乐观锁、悲观锁
      • 🪀可重入锁
      • 🪀读写锁 ReentrantReadwriteLock
      • 🪀分段锁
      • 🪀自旋锁
      • 🪀共享锁、独占锁
      • 🪀公平锁、非公平锁
        • 🪀针对synchronized锁的状态(底层monitorenter加锁、moniterexit释放锁)
        • 🪀ReentrantLock实现
        • 🏅AQS (AbstractQueuedSynchronizer,抽象同步队列 在JUC并发包下)
        • 🪂集合可以引申过来:ConcurrentHashMap
        • 🪂集合可以引申过来:CopyOnWriteArrayList
      • 🦓辅助类CountDownLatch
    • 🎀线程池
      • 🐕为什么要使用线程池?
      • 🐕你通常是怎么创建线程池的?为啥不使用其他的类Executors?
      • 🏨TreadPoolExecutor构造方法的七个参数
      • 🪂线程池拒绝策略handler
      • 🪂execute()与submit()的区别
      • 🪂关闭线程池shutdown、shutdownNow
    • 🎀ThreadLocal 本地线程变量
      • 🐕什么原因造成ThreadLocal内存泄漏问题?如何解决?

🐒个人主页:信计2102罗铠威

🏅JavaEE系列专栏

📖前言:

本篇博客主要总结面试中对线程知识的考察点

🎀多线程的优点、缺点

提高了程序的响应速度,可以多个线程各自完成自己的工作,以提高硬件设备的利用率。
缺点:可能会出现多个线程资源争夺问题,引发死锁。

🐕并发编程的核心问题 :不可见性、乱序性、非原子性

🪀不可见性

一个线程在自己的工作内存中对共享变量的修改,另外一个线程不能立即看到 。

🪀乱序性

为了优化性能,CPU有时候会改变程序中语句的先后顺序,但基本的程序逻辑不受影响。

🪀非原子性

线程切换带来的非原子性问题,A线程执行时,被切换到B线程执行,之后再A线程执行。 如果解决?加锁,如果只是++的话,JUC并发包下的原子类也可以。

🧸JMM(java内存模型)

java内存模型规定了所有的变量都存储在主内存中,每个线程还有自己的工作内存。线程先将主内存中的共享变量复制到自己的工作内存中去,在自己的工作内存中处理数据,再将处理好的结果写入主内存。

🏅volatile关键字:保证可见性、禁止指令重排序

一旦共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,
1.保证了一个线程修改了此变量,其他线程会立即可见(通过《缓存一致性协议》),保证可见性。
2.禁止指令重排序
3.但是volitile不能保证原子性

🐕CAS机制 (Conpare And Swap 比较并交换)

该算法采用自旋的思想,是一种轻量级锁机制。是(不加锁)乐观锁的实现。

线程先从主内存中获取共享变量复制到工作内存中作为预估值,再处理共享变量,得出结果。此时将线程中的预估值与主内存的值进行比较:
1.如果一样,证明没有其他线程干扰,将结果写入主内存。
2.如果不一样,重新获取预估值,重新进行计算结果,再次进行比较…

CAS缺陷:
由于CAS实现的是乐观锁,他会以自旋的方式不断的进行尝试,而不会像synchronized进行线程阻塞,当大量的线程自旋时,容易把CPU跑满。

原子类的实现是volatile +CAS机制。原子类适合在低并发的情况下使用。

🐕CAS会产生ABA问题

就是一个线程预估值为A,来一个线程将内存值改为B,再有一个线程将内存值又改为A。就不确定内存中的值是否其他线程进行干扰过了。
解决方案:
使用有版本号的原子类AtomicABA 根据版本号来解决问题

🏨java中锁的分类

🪀乐观锁、悲观锁

乐观锁: 它乐观认为对同一个数据的并发操作不会产生线程安全问题,不加锁,它会采用自选的方式,不断的尝试更新数据
悲观锁: 它悲观的认为对同一个数据的并发操作会产生线程安全问题,所以需要加锁进行干预
乐观锁适合“读多写少”的场景,悲观锁适合“读少写多”的场景

🪀可重入锁

就是一个加锁的方法可以调用进入另一个加锁的方法中去,两个方法可以共用同一把锁。可以在一定程度上避免死锁。

🪀读写锁 ReentrantReadwriteLock

可以实现读锁、写锁。读写可以使用一个锁实现。
特点:读读不互斥、读写互斥、写写互斥。
加读锁可以防止写,防止脏读。

🪀分段锁

1.8之后,分段锁是一种思想,减小锁的粒度, 将ConcurrentHashMap表每个区间的第一个节点当做锁对象,可以提高并发效率

🪀自旋锁

其实就是线程不断尝试获取锁的方式,不断的循环请求获取锁,直到抢到锁。会占用CPU资源。
例如:
原子类,需要改变主内存中的共享变量而不断尝试。
synchronized 加锁,其他线程不断获取锁,重复一定次数后会陷入阻塞。

🪀共享锁、独占锁

共享锁:多个线程共享一把锁,读写锁中的读锁,都是读操作时多个线程共享
独占锁:即排他锁,只允许一个线程使用该资源,写锁

🪀公平锁、非公平锁

公平锁:是指按照请求锁的顺序来分配锁,具有稳定获取锁的机会
非公平锁:不按照先来后到,谁抢到锁就是谁的。
对于synchronized是一种非公平锁。
而ReentrantLock默认是非公平锁,但是底层可以通过AQS机制来转化为公平锁

🪀针对synchronized锁的状态(底层monitorenter加锁、moniterexit释放锁)

锁的状态是根据对象头中的对象监视器来记录的,对象头Mark Word中记录对象的一些相关信息:hash值、分代年龄、锁的状态、偏向锁的线程id
无锁: 没有任何线程使用锁对象
偏向锁: 当前只有一个线程访问,那么锁对象会在对象头Mark Word中记录该线程id,下次自动获取锁,降低获取锁的难度。
轻量级锁: 当锁状态是偏向锁的时候,又有一个线程访问,此时锁的状态会升级成轻量级锁,其他线程会通过自旋的方式尝试获取锁,不会阻塞线程,提高性能。
重量级锁: 当锁的状态为轻量级锁时,线程自旋获取锁的次数达到一定次数时,锁的状态会升级成重量级锁。 会让自旋次数多的线程进入阻塞状态,以降低CPU的消耗
在这里插入图片描述

在这里插入图片描述

🪀ReentrantLock实现

在这里插入图片描述
。(ReentrantLock 中的内部抽象类sync继承了抽象同步队列AQS,里面定义了lock() 、unlock()方法)
()

🏅AQS (AbstractQueuedSynchronizer,抽象同步队列 在JUC并发包下)

是一个底层同步的实现者,有很多的同步类都继承了它。(ReentrantLock 中的内部类sync继承了它)
AbstractQueuedSynchronizer中有一个volatile修饰的int 类型的变量记录锁的状态(线程是否访问)
AQS里面还有一个内部类Node (双向链表,里面存放线程)
AQS里面获取锁状态方法getState()、修改锁状态方法通过CAS机制进行状态的更新

🪂集合可以引申过来:ConcurrentHashMap

聊到hashMap k-v存储
双列集合,键不能重复,值可以重复。它只适合单线程使用,在多线程情况下会报“并发修改异常”。他的键是由一个哈希表+链表的数据结构实现的。哈希表的初始大小为
16 ,通过对象哈希值%(哈希表长度-1)或者(哈希表长度-1)& 对象哈希值 确定对象在哈希表中的存储位置。如果出现哈希冲突了,通过拉链法解决,形成链表。
当哈希表存储超过0.75时,会进行2倍扩容。当每个节点上链表长度阈值超过8并且哈希表长度>=64,链表将转化为红黑树。当链表长度阈值为6时,红黑树会再次退化成链表。

hashTable它是线程安全的,因为它在每个方法上都加了synchronized锁,哪怕是在读方法上同样也上锁。虽然很安全,但是每个方法只允许一个线程进入,并发访问效率就比较低,适合并发量低的情况下。

在JUC并发包下还有一个ConcurrentHashMap的类。它是线程安全的。它采用了 CAS机制 +synchronized来保证线程安全。它是给节点加锁,降低了锁的粒度。
🏅put()时,先用key计算节点在哈希表中的位置,
它会来判断当前位置上有没有节点(null),如果没有,就使用CAS机制尝试放入。
如果有,就使用第一个节点作为锁对象,用synchronized加锁。这样就可以降低锁的粒度,可以同时有多个线程同时进入put()方法中,提高了并发的效率。但是如果线程在同一个位置操作,那必须还得一个一个来。
concurrentHashMap与hashTable都不允许存储null键,null值。
代码不允许,在源码中if(key==null ||value==null ) 抛出一个空指针异常

为什么不允许存储键为null或值为null?
为了消除歧义,因为如果值为null,不知道是原本Map中没有找到,还是本身就是null.。键同样也是这个道理,会不清楚传进去的键是因为其他原因报错导致的,还是本身就准备传进去一个为null的键。

🪂集合可以引申过来:CopyOnWriteArrayList

单列集合List
ArrayList 底层是数组实现的 ,可以存储重复元素,它是有序的(按添加顺序) , 可以实现动态扩容, 默认大小为10 , 可以进行1.5倍扩容,它是线程不安全的。
Vector 他是线程安全的 ,与ArrayList类似,默认大小为10 ,可以进行2倍扩容,它给每个方法上都加了synchronized锁。虽然它是安全的,但是锁是添加到方法上的,并发访问效率很低,适合并发量不大的情况。

JUC并发包下,CopyOnWriiteArrayList认为读取方法也加锁会造成浪费,因为读是不改变数据的。
它的读操作是不加锁的,并且为了尽可能的提高读的效率。对于改变数据的操作(插入 更新 )会先把原本数据复制到本地副本数组中进行修改,不影响原本数组,所以在写的过程中,也可以读数据。最后将修改好的本地副本数组直接替换为原本数组即可。(写的过程是加锁的,在方法里面使用了ReentrantLock锁,进行数组复制操作…)
它适合读多写少的场景。

CopyOnWriteArraySet底层基于CopyOnWriiteArrayList实现,线程安全的,不能存储重复的数据。

🦓辅助类CountDownLatch

它允许一个线程等待其他线程执行完成后再执行,底层是通过AQS来实现的。刚创建一个CountDownLatch对象时,指定一个初始化state表示需要等 待线程的数量,每当一个线程执行完成后 ,AQS的内部·state数量就减1。

🎀线程池

池:一个容器,将实现创建好的对象放到容器里面,待到使用的时候,不需要在去重新创建对象了,直接从池子里面拿,节省了创建对象的时间。用完不销毁,还放入池子中。

🐕为什么要使用线程池?

以前创建线程的时候,是直接创建线程,使用完后再销毁线程。如果线程比较多(测试5000个数据库连接对象),那么频繁的创建、销毁线程非常占时间,而使用线程池,可以省去频繁创建对象带来的时间开销,直接使用池子里面的,用完不销毁,在放入池子中,速度非常快。

🐕你通常是怎么创建线程池的?为啥不使用其他的类Executors?

通常使用TreadPoolExecutor类创建线程池的,为啥使用这个是因为我参考了《《阿里巴巴 java 开发规范》中推荐使用TreadPoolExecutor来创建线程,而不使用Executors来创建线程。因为TreadPoolExecutor类的构造方法中有七个参数配置,可以准确的配置对象的数量、最大等待的数量、拒绝策略…

🏨TreadPoolExecutor构造方法的七个参数

1.corePoolSize 核心池子中对象的数量
2. maximumPoolSize线程池最大线程对象数量
3. keepAliveTime非核心池对象多长空闲时间后销毁
4. unit:存活时间的单位
5. workQueue:阻塞队列,用于存放等待的线程
6. treadFactory线程工厂,主要用来创建线程
7. handler拒绝策略,表示拒绝处理任务时的策略
在这里插入图片描述

🪂线程池拒绝策略handler

  1. AbortPolicy 抛出异常
  2. CallerRunsPolicy 只要线程池未关闭,如果该任务被拒绝了,则由提交该任务的线程来执行此任务(例如main线程)
  3. DiscardOleddestPolicy 丢弃队列中等待时间最长的任务
  4. DiscardPolicy 直接丢弃任务,不予理会

🪂execute()与submit()的区别

execute( “实现Runnable接口任务” )返回值 void 适合不需要关注返回值的场景,
submit( “实现Callable接口任务” ) 返回值 Future 适合需要关注返回值的场景

🪂关闭线程池shutdown、shutdownNow

shutdown() 等待所有的任务执行完成,关闭线程池,在此期间不接受新的任务。
shutdownNow() 紧急关闭线程池,立即终止正在执行的线程,返回没有执行完任务的列表

🎀ThreadLocal 本地线程变量

是用来给每一个线程提供一个线程副本变量,使得每个线程中的变量是相互隔离的。
在ThreadLocal 底层源码中,它的内部维护了一个ThreadLocalMap内部类,如果为线程创建副本变量,就会判断该线程是否存在ThreadLocalMap
每个线程都有一个ThreadLocalMap属性,以ThreadLocal对象作为键,副本变量作为值。如果该线程已经有了ThreadLocalMap,那么就直接在Map中添加这个 ThreadLocal键 ,副本变量值 即可

🐕什么原因造成ThreadLocal内存泄漏问题?如何解决?

当本地变量不再使用时,由于ThreadLocalMap中这个vaule值还与外界保持着引用关系(强引用),这样一来,垃圾回收器就无法回收这个ThreadLocalMap对象了。
解决办法:
用完后就删除threadLocal.remove();
下次垃圾回收时,就可以回收ThreadLocalMap了。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【真机Bug】异步加载资源未完成访问单例导致资源创建失败

1.错误表现描述 抽卡时,10抽展示界面为A。抽取内容可能是整卡或者碎片,抽到整卡,会有立绘展示和点击详情的按钮。点击详情后出现详情页B。【此时界面A预制体被销毁,卡片数据进入数据缓存池】点击页面B的返回按钮,单例…

刚刚,OpenAI官方发文驳斥马斯克,自曝8年间邮件往来截图

文章开篇表示:「OpenAI 的使命是确保 AGI 惠及全人类,这意味着既要构建安全、有益的 AGI,又要帮助创造广泛的利益。我们正在分享我们在实现使命方面所学到的知识,以及有关我们与马斯克关系的一些事实。我们打算驳回马斯克的所有主…

回归啦!!!

消失的日子在实习,今天最后一天了来看看自己的学习日志,有没有可以和小伙伴交流的部分吧! 目录 一、产品one ①简介 ②底层原理 ③知识点一 作用一:日志采集 作用二:实时监测 作用三:规则匹配 作用…

Word中Endnote加载项不见了怎么处理?

打开word-①文件-②选项-③加载项 勾选endnote cite while you write 完成上述操作后,endnote便出现在菜单栏中。

[C语言]——分支和循环(2)

目录 一.逻辑操作符:&& , || , ! 1.逻辑取反运算符! 2.与运算符&& 3.或运算符 4.练习:闰年的判断 5.短路 二.switch语句 1.if语句和switch语句的对比 2.switch语句中的break 3.switch语句中的default 4.switch…

【C++】类的默认成员函数(上)

🔥博客主页: 小羊失眠啦. 🎥系列专栏:《C语言》 《数据结构》 《C》 《Linux》 《Cpolar》 ❤️感谢大家点赞👍收藏⭐评论✍️ 文章目录 一、默认成员函数二、构造函数构造函数的概念及特性 三、析构函数析构函数的特性…

论文笔记:Compact Multi-Party Confidential Transactions

https://link.springer.com/chapter/10.1007/978-3-030-65411-5_21 A compact, private, Multi-Party Confidential Transactions (MCT) 紧凑型多方机密交易(Compact MCT):MCT的长度与常规的单一所有者交易一样短;换句话说&…

【大数据架构(3)】Lambda vs. Kappa Architecture-选择你需要的架构

文章目录 一. Data Processing Architectures1. Lambda Architecture1.1. 架构说明a. Data Ingestion Layerb. Batch Layer (Batch processing)c. Speed Layer (Real-Time Data Processing)d. Serving Layer 1.2. Lambda Architecture的优缺点1.3. 使用案例 2. Kappa Architect…

MongoDB Helloworld For Window

1. 下载MongoDB Download MongoDB Community Server | MongoDB 2. 安装MongoDB 3. 创建DB. 4. 用java code 连接mongo. 做增删改查操作。 pom.xml <dependency><groupId>org.mongodb</groupId><artifactId>mongodb-driver-sync</artifactId>&…

docker 基础(二)

常见命令 Docker最常见的命令就是操作镜像、容器的命令&#xff0c;详见官方文档&#xff1a;https://docs.docker.com/ 数据卷 命令说明文档地址docker volume create创建数据卷docker volume createdocker volume ls查看所有数据卷docker volume lsdocker volume rm删除数…

0.8秒一张图40hx矿卡stable diffusion webui 高质极速出图组合(24.3.3)

新消息是。经过三个月的等待&#xff0c;SD Webui (automatic1111)终于推出了新版本1.8.0&#xff0c;本次版本最大的更新&#xff0c;可能就是pytorch更新到2.1.2, 不过还是晚了pytorch 2.2.2版。 不过这版的一些更新&#xff0c;在forget分支上早就实现了&#xff0c;所以。…

C及C++每日练习(2)

1.选择&#xff1a; 1.使用printf函数打印一个double类型的数据&#xff0c;要求&#xff1a;输出为10进制&#xff0c;输出左对齐30个字符&#xff0c;4位精度。以下哪个选项是正确的&#xff1f; A.%-30.4e B.%4.30e C.%-30.4f D.%-4.30 在上一篇文章中&#xff0c;提到了…

【C++实战项目】Date日期类 --- 运算符重载的深入探索

&#x1f4f7; 江池俊&#xff1a;个人主页 &#x1f525; 个人专栏&#xff1a;✅C那些事儿 ✅Linux技术宝典 &#x1f305; 此去关山万里&#xff0c;定不负云起之望 文章目录 引言一、为什么需要运算符重载&#xff1f;二、日期类的实现1. 基本框架2. 预备工作3. Date 类…

Linux Watchdog 机制是什么

当涉及到Linux操作系统的稳定性和可靠性时&#xff0c;Linux Watchdog机制是一个至关重要的议题。该机制旨在监控系统状态&#xff0c;确保在出现问题时采取适当的措施以维持系统的正常运行。本文将深入探讨Linux Watchdog机制的工作原理、应用范围以及如何配置和使用该机制来提…

【leetcode C++】电话号码的字母组合

17. 电话号码的字母组合 题目 给定一个仅包含数字 2-9 的字符串&#xff0c;返回所有它能表示的字母组合。答案可以按 任意顺序 返回。 给出数字到字母的映射如下&#xff08;与电话按键相同&#xff09;。注意 1 不对应任何字母。 题目链接 . - 力扣&#xff08;LeetCode&…

MyBatis 学习(六)之动态 SQL

目录 1 动态 SQL 介绍 2 if 标签 3 where 标签 4 set 标签 5 trim 标签 6 choose、when、otherwise 标签 7 foreach 标签 8 bind 标签 1 动态 SQL 介绍 动态 SQL 是 MyBatis 强大特性之一&#xff0c;极大的简化我们拼装 SQL 的操作。MyBatis 的动态 SQL 是基于 OGNL 的…

腾讯云8核32G22M服务器优惠价格115元1个月、345元3个月

腾讯云8核32G22M服务器优惠价格115元1个月、345元3个月 一张表看懂腾讯云服务器租用优惠价格表&#xff0c;一目了然&#xff0c;腾讯云服务器分为轻量应用服务器和云服务器CVM&#xff0c;CPU内存配置从2核2G、2核4G、4核8G、8核16G、4核16G、8核32G、16核32G、16核64等配置可…

基于springboot+vue的科研工作量管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…

基于 HBase Phoenix 构建实时数仓(1)—— Hadoop HA 安装部署

目录 一、主机规划 二、环境准备 1. 启动 NTP 时钟同步 2. 修改 hosts 文件 3. 配置所有主机间 ssh 免密 4. 修改用户可打开文件数与进程数&#xff08;可选&#xff09; 三、安装 JDK 四、安装部署 Zookeeper 集群 1. 解压、配置环境变量 2. 创建配置文件 3. 创建新…

Vue+腾讯地图-实现关键词输入提示功能

不废话&#xff0c;上代码~~~ 效果图&#xff1a; 1、先去腾讯地图后台创建自己的应用获取到应用的 Key 腾讯地图后台地址&#xff1a;腾讯位置服务 - 立足生态&#xff0c;连接未来 创建应用的 Key 如下&#xff1a; 2、在项目中添加腾讯地图API的js插件&#xff0c;如…