多线程进阶

多线程常见面试题

文章目录

  • 多线程常见面试题
    • 1. 常见的锁策略
      • 1.1乐观锁&悲观锁
      • 1.2 轻量级锁&重量级锁
      • 1.3 自旋锁&挂起等待锁
      • 1.4 读写锁&普通互斥锁
      • 1.5 公平锁&非公平锁
      • 1.6可重入锁&不可重入锁
    • 2. CAS
    • 3. Sychronized原理
      • 3.1 锁升级
      • 3.2 锁消除
      • 3.3 锁粗化
    • 4. JUC
      • 4.1 Callable接口(创建线程的方式)
      • 4.2 创建线程的几种方式
      • 4.3 ReentrantLock
    • 5. JUC工具类
      • 5.1 Semaphore-信号量
      • 5.2 CountDownLatch
      • 5.3 CyclicBarrier - 循环栅栏
    • 6.线程安全的集合类
    • 7.多线程使用队列
    • 8.多线程环境使用哈希表
    • 9.死锁

1. 常见的锁策略

1.1乐观锁&悲观锁

在加锁的态度的角度去执行加锁逻辑

乐观锁:在获取锁的时候预期这个锁竞争不太激烈,那么就可以先不加锁,或者少加锁(有真实的竞争再来加锁)

悲观锁:在获取锁的时候预期这个锁竞争非常激烈,那么就必须先加锁再执行任务

1.2 轻量级锁&重量级锁

站在加锁过程角度的描述

轻量级锁:加锁的过程比较简单,用到的资源比较少,典型就是用户态的一些加锁操作(在Java层面就可以完成加锁)

重量级锁:加锁的过程比较复杂,用到的资源比较多,典型就是内核态的一些操作

乐观锁是能不加就不加,从而导致他干的活就比较少了,那么他消耗的资源就比较少了, 从而可以说乐观锁是一个轻量级锁

悲观锁是不管怎么样都先把锁加上,从而导致他干的活多了,那么消耗的资源就比较多了,可以说被悲观锁也是一种重量级锁

1.3 自旋锁&挂起等待锁

自旋锁:不停的检查锁是否被释放,如果一旦锁被释放掉那么就直接获取锁资源

自旋锁的优点:

  1. 他是一个纯用户态的操作,比较轻量
  2. 锁一旦被释放就可以马上知道

还有一些缺点:

  1. 不停的循环比较浪费系统的资源

    可以通过控制自旋的次数来提高效率,并且避免系统资源的过度浪费

挂起等待锁:不主动询问锁资源,而是让系统调度去竞争锁资源

  1. 通过阻塞与就绪状态的切换来获取锁资源
  2. 如果锁一旦释放,没有办法立马知道
  3. 是通过系统内核来处理的

自旋锁是一种典型的轻量级锁的具体实现

挂起等待锁是一种典型的重量级锁的具体实现

1.4 读写锁&普通互斥锁

读写锁:在锁中标识读或写,在竞争时根据这个标识来判断是否参与竞争

读的时候加读锁(共享锁),多个锁可以共存,同时多个读锁是互不影响的

写的时候加写锁(排他锁),只能有一个写锁在执行任务,和别的锁是冲突的

写锁和写锁不能共存

写锁和读锁不能共存

读锁和读锁可以共存

为什么要用读写锁

在我们的程序中可能会出现激烈的锁竞争,但是获取锁之后的操作,有可能是大量的读操作,读操作又不涉及修改

所以所有的读都可以并发执行,只有读的时候不让别的线程来修改就可以了

如果一个操作是读操作,当前也是读锁,那么就直接读了,不产生锁竞争,从而节省了资源

如果一个操作是写操作,这个时候就让他等待

针对高并发读的业务场景中,锁竞争就会大大降低,从而提交程序的运行效率

互斥锁

有竞争关系,只能一个线程释放锁之后,别的线程再来抢

1.5 公平锁&非公平锁

公平锁:先来后到,先排队的线程先获取到锁,后排队的后获取到锁

非公平锁:谁先抢到就是谁的

在Java JUC中有一个类实现了公平锁

synchronized是一个非公平锁

1.6可重入锁&不可重入锁

可重入锁:对一把锁可以连续加锁多次,而不造成死锁

不可重入锁:对一把锁连续加锁多次,造成死锁

对比sychronized

乐观锁&悲观锁即是乐观锁也是悲观锁
轻量级锁&重量级锁即是轻量级锁也是重量级锁
自旋锁&挂起等待锁即是自旋锁也是挂起等待锁
读写锁&普通互斥锁是互斥锁
公平锁&非公平锁是非公平锁
可重入锁&不可重入锁是可重入锁

当锁竞争不激烈时,是一个乐观锁,轻量级锁,自旋锁

如果竞争不激烈这个状态就一直保证

如果锁竞争激烈的时候,就会升级为悲观锁,重量级锁,挂起等待锁

2. CAS

compare and swap 比较并交换

首先比较当前变量值与某个值期望的值是否相同,如果相同则用一个新的值变量内存中的值

boolean CAS(address,exceptValue,swapValue){if(&address==exceptValue){&address=swapValue;return true;}return false;
}//核心逻辑
if(value==exceptValue){value=oldValue;
}
  1. 用一个预期值去和内存中的值做比较
  2. 如果预期值与内存中的值相等,那么就用新值更新内存中的值
  3. 如果预期值与内存中的值不相等,那么就不做任何操作

画图演示

1.两个线程读取value的值到oldvalue

image-20230909134802205

  1. 线程1执行CAS操作,由于oldvale与value值相等。直接对value赋值

    image-20230909141143120

  2. 线程2再执行CAS操作,第一次CAS时发现oldCValue和value不相等,不能进行赋值。因此需要进入循环(自旋),在循环里重新读取value的值赋给oldValue

    image-20230909141415610

  3. 线程2接下来第二次执行CAS,此时oldValue与value相同,于是直接执行赋值操作

    image-20230909141457567

CAS操作直接修改的是内存中的值,每次都会去读,去比较去修改指定内存地址的值,从而保证了原子性的操作

CAS中ABA的问题

3. Sychronized原理

Sychronize本身会对锁做一些智能的优化,在程序不同的运行时期,适应不同的锁策略

3.1 锁升级

无锁——>偏向锁(JVM启动4秒后,创建的锁对象才会偏向状态)——>轻量级锁——>重量级锁

无锁:没有锁竞争时

偏向锁:只是给锁对象中加入了一个标签,并没有真正的去加锁

轻量级锁:通过自旋锁实现用户态的操作

重量级锁:内核态的加锁操作,调用的是CPU加锁指令

3.2 锁消除

sychronized的一种优化策略

sychronized是程序员自己手动加的获取锁的逻辑,什么时候加,加在哪个代码块,JVM管不了,但是在编译和运行的时候,JVM可以知道程序是读变量,还是写变量

如果程序员对所有的读操作都加了sychronized关键字,但是又没有写操作,那么这是JVM就认为这个锁是多余的,那么sychronized就不会真正的去加锁了,这个现象称为锁消除

在多线程状态下,多个线程对同一变量进行修改才会又线程安全问题,多个线程读一个变量没有线程安全问题

3.3 锁粗化

锁的粒度,也就是锁的范围,也就是sychronized包裹代码的多少,包的越多粒度就越粗,包的越少粒度就越细,对于多个连续的任务,如果每个任务都加一把锁,这个过程就会产生频繁的锁竞争,JVM就会把锁的范围增大到整个任务的开始与结束,减少锁竞争的次数来提高效率。

4. JUC

4.1 Callable接口(创建线程的方式)

描述线程要执行的任务

Callable和Runnable有什么区别

  1. Callable要实现call()且有返回值,Runnable方法要实现run(),没有返回值
  2. Callable的call()方法可以抛出异常,Runnable的Run()方法不能抛出异常(业务异常)
  3. Callable要搭配FutureTask一起使用,通过futureTask.get()来获取call()的返回值
  4. 两者都是描述线程任务的接口

4.2 创建线程的几种方式

创建线程的几种方式

  1. 继承Tread类,实现run()方法
  2. 实现Runnable接口,实现run()方法
  3. 实现Callable接口,实现call()方法
  4. 通过线程池,提交任务

由于Runnable和Callable都是函数式接口,我们可以通过Lambda表达式的方式简化写法,也可以通过匿名内部类的方式来简化写法

4.3 ReentrantLock

JUC中重要的一个技术点

lock()加锁

unLock()解锁

tryLock()尝试解锁

sychrinized与ReentrantLock区别

  1. sychronized退出代码块就自动释放锁,ReentrantLock必须手动释放锁,注意要是有try/finallu处理加锁释放锁的代码
  2. sychrinized是非公平锁,ReentrantLock即是非公平锁也是公平锁
  3. ReentrantLock可以根据不同的条件去进行休眠和唤醒
  4. sychronized在申请锁失败时,会一直等待锁资源,而ReetrantLock可以通过tryLock的方式等待一段时间就放弃
  5. sychronized是JVM的一个对锁的实现,最终调用CPU加锁指令,而ReetrantLock是Java层面的JUC包中的一组实现类

5. JUC工具类

5.1 Semaphore-信号量

申请资源的操作称为P操作,资源数量减一,当减到0的时候其他的线程就要等待

释放资源的操作称为V操作,资源数量就要加一

信号量本质上就是要维护资源的数量

5.2 CountDownLatch

等待所有线程全都完成任务后才执行后面的操作

5.3 CyclicBarrier - 循环栅栏

CountDownLatch的进级版,可以实现线程间的相互等待,计数重置

6.线程安全的集合类

Vector,Stack,HashTable

如何在多线程环境下保证集合类的线程安全

  1. 手动加锁,sychronized包裹代码块,ReentrantLock 加锁解锁
  2. 使用工具类Collections.synchronizedList(array)(不常用)
  3. JUC 提供了集合类CopyOnWriteArraylist

7.多线程使用队列

  1. ArrayBlockingQueue基于数组实现的队列
  2. LinkedBlockingQueue基于链表实现的队列
  3. PriorityBlockingQueue 基于堆实现的优先级队列
  4. TransferQueue最多只包含一个元素的阻塞队列

8.多线程环境使用哈希表

HashTable JDK1.0提供 线程安全不推荐使用

HashMap线程不安全

面试题

  1. 多线程环境下,用哪个类来保证Map的线程安全

    JUC包下的ConcurrentHashMap

  2. ConcurrentHashMap与HashMap/HashTable 的区别

    1. HashMap是线程不安全的
    2. HashTable是线程安全的,但是不推荐使用,因为所有的操作都加锁了
    3. ConcurrentHashMap的锁粒度比较小,不是对整个Hash表加锁,而是对每一个数据的下标进行加锁
    4. ConsurrentHashMap对写操作加锁,对读操作不加锁
    5. 对于共享变量大量运行了volatile关键字去修饰
    6. ConcurrentHashMap对扩容进行了优化

    扩容

    HashMap和HashTable在扩容时,重新创建一个容量是当前容量2倍的新数组,把所有的元素全部重新hash到新数组里去,是一个完整的操作,效率并不是很高

    ConcurrentHashMap,创建一个新数组容量是原来的2倍,原数组与新数组同时存在一段时间,每次调用ConcurrentHashMap方法的时候,都去搬运一部分元素到新数组中,当原数组中的数据搬运完后,原数组就可以删除了,只使用新数组就可以了

    当两个数组同时存在的时候,查询数据就需要在两个Map中同时查询

    删除时在两个Map中查找,找到后删除

    写入时只往新的Map中去写

    典型的以空间换时间的做法,浪费了一些存储空间,但提高了运行效率

9.死锁

在多线程环境中遇到的最严重的问题之一,线程在获取锁资源的时候,由于获取不到导致线程卡死(阻塞),不执行了

造成死锁的原因

  1. 资源互斥:线程1拿到了锁A,线程2不能同时得到锁
  2. 不可抢占:获取到锁的线程,除非自己主动释放锁,别的线程不能从他的手里抢过来
  3. 保持与请求:线程1已经获得了锁A,还要在这个基础再去获取锁B
  4. 循环等待:线程1等待线程2释放锁,线程2等待线程3释放锁,线程3等待线程1释放锁……

以上四条是造成死锁的必要条件,必须同时满足

如何解决死锁

  1. 互斥访问:锁的基本特性,不能打破
  2. 不可抢占:锁的基本特性,不能打破
  3. 保持与请求:和代码实现或是设计的角度来说是可以改变保持与请求的顺序,也就是获取锁的顺序
  4. 循环等待:最有可能也是最常见的解决死锁的策略就是打破循环等待

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

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

相关文章

支持多校 微信课表小程序源码 排课小程序源码 支持导入课表 情侣课表 背景设置

练手Lab课程表小程序源码是一个基于thinkphp系统进行开发的前后端分离系统。 源码功能介绍 1、情侣功能 2、情侣间留言 3、情侣间互相设置课程表背景 4、自己日、周课程表背景设置 5、教务系统课程表导入 6、导入别人分享的课表 7、导入别人分享的单课 8、多校支持 9…

AIR101 LuatOS LVGL 显示多个标签例程

屏幕资料 AIR101与屏幕连接 PC端仿真环境合宙官方PC端版本环境搭建教程 PC电脑仿真 -- sys库是标配 _G.sys require("sys") sys.taskInit(function()local cnt0lvgl.init(480,320)--lvgl初始化local cont lvgl.cont_create(nil, nil);-- lvgl.cont_set_fit(cont, …

Centos8 降低gcc版本至gcc-7.3

1 首先卸载系统中的gcc sudo yum remove gcc 2 重新安装gcc-7.3 sudo dnf group install “Development Tools” 然后再次卸载gcc sudo yum remove gcc 然后发现centos-release-scl-rh已经安装了 sudo yum install centos-release-scl-rh yum -y install devtoolset-7-gcc dev…

不希望你的数据在云中?关闭iPhone或Mac上的iCloud

​如果你不想使用iCloud,可以很容易地从设备设置中选择退出并关闭它。当你禁用iCloud时,它会删除该设备对iCloud的访问,但不会删除苹果服务器上的任何数据。我们将在本文末尾向你展示如何做到这一点。 注销iCloud并完全禁用它 如果你根本不…

HDR图像处理软件 Photomatix Pro mac中文版新增功能

Photomatix Pro mac是一款专业的HDR合成软件,可以将不同曝光的多张照片合成为一张照片,而保留更多的细节。并且合成时可以帮助去除照片中的鬼影。Photomatix Pro提供两种类型的过程来增加动态范围,一个过程称为HDR色调映射,另一个…

YOLOv5算法改进(20)— 如何去写YOLOv5相关的论文(包括论文阅读+规律总结+写作方法)

前言:Hello大家好,我是小哥谈。最近一直在阅读关于YOLOv5的相关论文,读着读着我发现一条可以发论文的规律,特此简单总结一下,希望能够对同学们有所启迪!🌈 前期回顾: YOLOv5算法改进(1)— 如何去改进YOLOv5算法

kafka入门03——简单实战

目录 安装Java 安装Zookeeper 安装Kafka 生产与消费 主要是记录下Kafka的安装配置过程,前置条件需要安装jdk和zookeeper。 安装Java 1.Oracle官网下载对应jdk安装包 官网地址:Java Downloads | Oracle 好人分享了下载需要的oracle账号&#xff0c…

WPS中图的自动编号及引用

WPS中图的自动编号及引用 图的自动编号图编号的引用图编号及引用的更新 图的自动编号 将光标放置在需要插入编号的位置点击“引用”→“题注”: 点击“引用”→“题注”: 点击“编号”,设置图的编号格式,可勾选“包含章节编号”&…

Plex Media Server for Mac: 打造您的专属媒体库

在数字媒体时代,我们越来越依赖各种媒体应用来丰富我们的生活。其中,Plex Media Server for Mac以其高效、稳定和多功能性,逐渐成为了Mac用户们的首选。今天,我们就来深入探讨这款个人媒体软件的优势和应用场景。 Plex Media Serv…

防止消息丢失与消息重复——Kafka可靠性分析及优化实践

系列文章目录 上手第一关,手把手教你安装kafka与可视化工具kafka-eagle Kafka是什么,以及如何使用SpringBoot对接Kafka 架构必备能力——kafka的选型对比及应用场景 Kafka存取原理与实现分析,打破面试难关 防止消息丢失与消息重复——Kafka可…

Netty实战-实现自己的通讯框架

通信框架功能设计 功能描述 通信框架承载了业务内部各模块之间的消息交互和服务调用,它的主要功能如下: 基于 Netty 的 NIO 通信框架,提供高性能的异步通信能力;提供消息的编解码框架,可以实现 POJO 的序列化和反序…

BurpSuite安装

下载 BurpSuite 下载 Java17 下载后确定版本 java -version获取启动器 密钥生成器 破解 将下载的 BurpSuite、启动器、密钥生成器,放入同一个目录 打开 CMD 进入该目录 启动密钥生成器 java -jar burp-keygen-scz.jar开启新的CMD,进入该目录 启动…

IOS屏幕旋转监听

IOS屏幕旋转 1.设计窗口,添加三个按钮 2.添加事件连接 3.按钮点击事件实现 先添加三个IBAction 实现IBAction 使用旋转立刻生效 -(IBAction)btnFixPortrait:(id)sender{//访问应用程序委托成员_app.mask UIInterfaceOrientationMaskPortrait;//设置窗口旋转属性[self setN…

企业安全—SDL概述篇

0x00 前言 众所周知,从源头开始就开发安全的代码,比产品已经成型之后付出的代价要小很多,也就是一直在说的安全左移的概念。最好就是从一开始,大家就用最安全的代码,或者是框架,那么开发出来的产品必然会减…

进程的优先级与LAMP项目部署实战

一、进程的优先级(扩展) 1、什么是进程的优先级 Linux是一个多用户、多任务的操作系统,系统中通常运行着非常多的进程。哪些进程先运行,哪些进程后运行,就由进程优先级来控制 思考:什么时候需要用到进程…

力扣-python-两数相加

题解 1: # Definition for singly-linked list. # class ListNode(object): # def __init__(self, val0, nextNone): # self.val val # self.next nextclass Solution(object):def addTwoNumbers(self, l1, l2):""":type l1: ListNode:t…

【MATLAB源码-第56期】基于WOA白鲸优化算法和PSO粒子群优化算法的三维路径规划对比。

操作环境: MATLAB 2022a 1、算法描述 1.粒子群算法(Particle Swarm Optimization,简称PSO)是一种模拟鸟群觅食行为的启发式优化方法。以下是其详细描述: 基本思想: 鸟群在寻找食物时,每只鸟都…

2023-10-17 LeetCode每日一题(倍数求和)

2023-10-17每日一题 一、题目编号 2652. 倍数求和二、题目链接 点击跳转到题目位置 三、题目描述 给你一个正整数 n ,请你计算在 [1,n] 范围内能被 3、5、7 整除的所有整数之和。 返回一个整数,用于表示给定范围内所有满足约束条件的数…

动手学深度学习—含并行连结的网络GoogLeNet(代码详解)

目录 1. Inception块3. GoogLeNet模型3. 训练模型 GoogLeNet吸收了NiN中串联网络的思想,并在此基础上做了改进,并且在2014年的ImageNet图像识别挑战赛中获得了不错的效果。 1. Inception块 GoogLeNet论文解决了多大的卷积核最合适的问题。 Inception块…

PHP 在线考试管理系统mysql数据库web结构layUI布局apache计算机软件工程网页wamp

一、源码特点 PHP 在线考试管理系统是一套完善的web设计系统 layUI技术布局 ,对理解php编程开发语言有帮助,系统具有完整的源代码和数据库,系统主要采用B/S模式开发。 PHP 在线考试系统1 代码 https://download.csdn.net/download/qq_41…