面试问答总结之并发编程

文章目录

  • 🐒个人主页
  • 🏅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内存泄漏问题?如何解决?

🐒个人主页

🏅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了。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux - 权限概念

Linux下有两种用户:超级用户(root)、普通用户。 超级用户:可以再linux系统下做任何事情,不受限制普通用户:在linux下做有限的事情超级用户的命令提示符是“#”,普通用户的命令提示符是“$” 命…

深入探究Python多进程编程:Multiprocessing模块基础与实战【第98篇—Multiprocessing模块】

深入探究Python多进程编程:Multiprocessing模块基础与实战 在Python编程中,多进程处理是一项关键的技术,特别是在需要处理大规模数据或执行耗时任务时。为了充分利用多核处理器的优势,Python提供了multiprocessing模块&#xff0…

G8-ACGAN理论

本文为🔗365天深度学习训练营 中的学习记录博客 原作者:K同学啊|接辅导、项目定制 我的环境: 1.语言:python3.7 2.编译器:pycharm 3.深度学习框架Pytorch 1.8.0cu111 一、对比分析 前面的文章介绍了CGAN&#xf…

Python批量提取文件夹中图片的名称及路径到指定的.txt文件中

目录 一、代码二、提取效果 一、代码 import os# 定义要保存的文件名 file_name "TestImage/Image_Visible_Gray.txt"# 读取文件夹路径 folder_path "TestImage/Image_Visible_Gray"# 遍历文件夹中的所有文件 with open(file_name, "w") as f…

132557-72-3,2,3,3-三甲基-3H-吲哚-5-磺酸,具有优异的反应活性和光学性能

132557-72-3,5-Sulfo-2,3,3-trimethyl indolenine sodium salt,2,3,3-三甲基-3H-吲哚-5-磺酸,具有优异的反应活性和光学性能,一种深棕色粉末 您好,欢迎来到新研之家 文章关键词:132557-72-3,5…

使用maven项目引入jQuery

最近在自学 springBoot ,期间准备搞一个前后端不分离的东西,于是需要在 maven 中引入jQuery 依赖,网上百度了很多,这里来做一个总结。 1、pom.xml 导入依赖 打开我们项目的 pom.xml 文件,输入以下坐标。这里我使用的是…

[BUUCTF]-Reverse:reverse3解析

查看ida 从下图的/3和*4可以推断得出来是base64加密。 ida里大致意思就是我们输入的字符串经过base64加密,循环递减,最后等于str2,那我们输入的字符串就是flag。 完整exp: import base64 liste3nifIH9b_CndH print(len(list))fl…

2024年3月腾讯云服务器优惠价格曝光!这价格没谁了

腾讯云优惠活动2024新春采购节活动上线,云服务器价格已经出来了,云服务器61元一年起,配置和价格基本上和上个月没什么变化,但是新增了8888元代金券和会员续费优惠,腾讯云百科txybk.com整理腾讯云最新优惠活动云服务器配…

深度伪造,让网络钓鱼更加难以辨别

网络钓鱼一直是安全领域的一个突出话题,尽管这类诈骗形式已经存在了几十年,依旧是欺诈攻击或渗透组织的最有效方法之一。诈骗分子基于社会工程原理,通过邮件、网站以及电话、短信和社交媒体,利用人性(如冲动、不满、好…

智能分析网关V4电瓶车检测与烟火算法,全面提升小区消防安全水平

2024年2月23日,南京市某小区因电瓶车停放处起火引发火灾事故,造成巨大人员伤亡和损失。根据国家消防救援局的统计,2023年全国共接报电动自行车火灾2.1万起。电瓶车火灾事故频发,这不得不引起我们的重视和思考,尤其是在…

用于游戏开发的顶级 PYTHON 框架

一、说明 我们试图用python开发游戏,一旦产生这个念头,就伴随这样一个问题:当今用于构建游戏的领先 Python 框架有哪些?python下,支持游戏开发平台有哪些优势?我们在这篇博文中告诉你。 二、高级游戏平台简…

小甲鱼Python07 函数初级

一、创建和调用函数 pass语句表示一个空的代码块,我们经常先写好函数,pass占一个坑,等规划好之后再来填坑。 函数也是可以指定参数的,我们会把参数传进去用来替代形参。 在Python里如果想要返回值,不需要指定函数的返…

高侧开关芯片四通道 40V 50mΩ车规级带反向电流保护功能负载检测高边开关

概述 PC8845/G是四通道、高侧功率具有集成NMOS功率FET的开关,以及电荷泵。该设备集成了高级 保护功能,例如负载电流限制,通过功率限制进行过载主动管理可配置闩锁关闭的超温停机。全面诊断和高精度电流感应这些功能实现了对负载的智能控制。…

2326. 王者之剑(网络流,最小割,最大权独立集,最小点权覆盖)

活动 - AcWing 给出一个 nm 网格,每个格子上有一个价值 vi,j 的宝石。 Amber 可以自己决定起点,开始时刻为第 0 秒。 以下操作,在每秒内按顺序执行。 若第 i 秒开始时,Amber 在 (x,y),则 Amber 可以拿走 (x,y) 上的…

LeetCode刷题--- 乘积为正数的最长子数组长度

个人主页:元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归算法题 http://t.csdnimg.cn/yUl2I 【C】 ​​​​​​http://t.csdnimg.cn/6AbpV 数据结构与算法 ​​​http://t.csdnimg.cn/hKh2l 前言:这个专栏主要讲述动…

3.1作业

作业要求&#xff1a; 通过w(红色臂角度增大)s&#xff08;红色臂角度减小&#xff09;d&#xff08;蓝色臂角度增大&#xff09;a&#xff08;蓝色臂角度减小&#xff09;按键控制机械臂 程序代码&#xff1a; #include<myhead.h> #define SER_IP "192.168.126.…

AGI概念与实现

AGI AGI&#xff08;Artificial General Intelligence&#xff09;&#xff0c;中文名为“通用人工智能”或“强人工智能”&#xff0c;是指通过机器学习和数据分析等技术&#xff0c;使计算机具有类似于人类的认知和学习能力的技术. 多模态的大模型 &#xff08;Multimodal…

VSCode上搭建C/C++开发环境(vscode配置c/c++环境)Windows系统---保姆级教程

引言劝退 VSCode&#xff0c;全称为Visual Studio Code&#xff0c;是由微软开发的一款轻量级&#xff0c;跨平台的代码编辑器。大家能来搜用VSCode配置c/c&#xff0c;想必也知道VSCode的强大&#xff0c;可以手握一个VSCode同时编写如C&#xff0c;C&#xff0c;C#&#xff…

Java SE:多线程(Thread)

1. 线程两个基本概念 并发&#xff1a;即线程交替运行多个指令并行&#xff1a;即多个线程同时运行指令 并发并行不矛盾&#xff0c;两者可同时发生&#xff0c;即多个线程交替运行指令 2. 多线程3种实现方式 2.1 直接创建线程对象 /*** 方式1&#xff1a;* 1. 创建thread类的…

太实用了!微信自动回复神器,助你轻松社交

在当今社交网络的时代&#xff0c;微信已经成为了一种重要的社交工具&#xff0c;为了更有效地管理微信号和提高社交效率&#xff0c;许多人开始使用微信管理系统&#xff0c;下面就一起来看看它的优势吧。 首先&#xff0c;使用微信管理系统可以实现多个微信号同时登陆&#…