多线程、进程、线程五种状态、synchronized、volatile、Lock、CAS、死锁、ThreadLocal

1、并发编程

并发编程三要素

  • 原子性:只一个操作要么全部成功,要么全部失败
  • 可见性:一个线程对共享变量的修改,其他线程能够立刻看到
  • 有序性:程序执行的顺序按照代码的先后顺序执行

synchronized,Lock解决原子性问题
volatile,,Lock解决可见性和有序性问题

2、线程和进程的区别

  • 根本区别:进程是操作系统资源分配的最小单位;线程是处理任务调度和执行的最小单位
  • 资源开销:每个进程都有单位的空间,进程间的切换有较大的开销;线程是轻量级的进程,同一类线程共享数据空间,每个线程有独立的运行栈和程序计数器,线程之间的切换开销较小
  • 包含关系:进程包含线程
  • 内存分配:线程共享地址空间和资源,进程之间的地址空间和资源相互独立
  • 影响关系:一个进程崩溃,在保护模式下其他进程不受影响;一个线程崩溃,其他线程也会受影响,整个进程都会崩溃。多进程要比多线程更加健壮
  • 执行关系:进程有独立的执行入口,线程不能独立执行,必须依赖进程

上下文切换:任务从保存到再加载的过程就是一次上下文切换。

3、创建线程的四种方式

  • 继承Thread
  • 实现Runnable接口
  • 实现Callable接口
  • 使用Executors工具类创建线程池

Runnable和Callable区别

相同点:

  • 都是接口;
  • 都可以编写多线程程序;
  • 都采用Thread.start()启动线程

不同点

  • Runnable接口的run()方法没有返回值 ;Callable()接口的call()方法有返回值,是个泛型
  • Runnable接口run()方法只能抛出运行时异常,且无法捕捉处理;Callable接口call()方法允许抛出异常,可以获取异常信息

线程的run()和start()有何区别

  • start()用于启动线程,run()用于执行线程运行时的代码。run()方法可以重复调用,start()只能调用一次
  • start()方法用于启动线程,真正实现了多线程的运行。调用start()时无需等待run()方法方法体代码执行完毕就可以执行其他代码;此时线程是就绪态,并没有开始运行,然后通过Thread类调用run()方法完成其运行状态,run()方法运行结束,此线程就终止了
  • run()方法是在线程里的,直接调用run()方法,相当于调用了一个普通的函数,必须等待run()方法执行完毕才可以执行下面的代码,所以执行路径还是一条,没有多线程的特征,所以在线程启动时,要调用start()方法而不是run()方法。

Future和FutureTask

  • Callable接口的call()方法有返回值,Future可以拿到异步执行任务(这个任务也许并没有完成)的返回值,并且可以抛出异常信息。
  • FutureTask是Future的具体实现。FutureTask实现了RunnableFuture接口。RunnableFuture接口又同时继承了Future和 Runnable接口。所以FutureTask既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值。

4、线程的状态和基本操作

线程的五种状态和生命周期

在这里插入图片描述

  • 新建(new)
  • 可运行/就绪态(runnable):调用start()方法后就处于Runnable态
  • 运行(running):runnable态获取到时间片,就进入running态;就绪态是进入运行态的唯一入口,线程要进入运行态就必须要进入就绪态
  • 阻塞(block)
  • 死亡(dead):死亡的线程不可复生

线程调度的方法

1、wait():使一个线程处于阻塞等待状态,并且释放所持有的对象锁
2、sleep():使役个正在运行的线程处于睡眠状态,是一个静态方法
3、notify():唤醒一个处于等待队列的线程,再调用此方法时,并不能确切的唤醒某个等待的线程,由JVM确定唤醒哪个线程,并且与优先级无关
4、notifyAll():唤醒所有处于等待队列的线程,然后重新竞争锁

wait()和sleep()区别

  • 所在类不同:sleep()是Thread类的静态方法;wait()是Object类的方法
  • 锁:sleep()不释放锁,wait()释放锁
  • 用途:wait()用于线程之间通信;sleep()用于暂停线程执行
  • 用法不同:wait()在结束后,不会自动苏醒,需要别的线程调用同一个对象上的notify()或者notifyAll()去唤醒;而sleep()方法在结束后,自动苏醒。

interrupt()、interrupted()、isInterrupted()

  • interrupt()、isInterrupted()是通过Thread对象调用,是实例方法;interrupted()是通过Thread类调用,是静态方法
  • interrupt()方法,只是通知该线程停止运行,只是通知,并没有直接中断,而是由程序自己决定是否中断

线程类的构造方法、静态代码块是被new这个线程的类的线程所调用的(谁new谁调用),run()方法是自身线程调用

5、synchronized

作用:用来控制线程同步的,被synchronized修饰的代码不能被多个线程同时执行,可以修饰类、方法、变量

synchronized底层原理

synchronized修饰的代码在反编译为字节码文件时,前后都出现了monitor字样,前面出现的是monitorenter,后面出现的是monitorexit,就是释放锁。当执行monitorenter时,当前线程试图获取对象锁所持有的monitor,当计数器为0时,就可以成功获取,当获取到时,计数器+1。并且就算当前线程已经拥有对象锁的monitor的持有权,那么就可以重入这个monitor,重入计数器也会加一。如果其他线程占有monitor持有权,那么当前线程就会阻塞,知道其他线程执行monitorexit,执行后锁释放,计数器设置为0。

自旋

即其他线程不进入阻塞态,而是在synchronized边界循环等待,不断尝试获取锁,这就是自旋

synchronized锁升级

synchronized涉及到用户态和内核态的切换,在1.6之前,锁都是重量级锁,即我们不管什么线程来操作资源,都要进行加锁释放锁,如果有多线程,还要等待之类的,很浪费资源,1.6之后引入了偏向锁与轻量锁来减小获取和释放锁所带来的性能消耗。

锁升级其实就是对synchronized的优化,以前用synchronized修饰一个对象或者是方法,方法也等于是锁住对象,直接用一把操作系统层面的大锁,万一只有少量线程的话会大题小作了,如果大量线程的话又会特别消耗时间,划不来,所以要将以前的二话不说用一把大锁进行优化。

无锁->偏向锁->轻量级锁->重量级锁

原理:在锁对象的对象头有一个threadid字段,第一次访问时threadid为空,JVM让其持有偏向锁,并把threadid设置为线程id,再次进入时只需要判断两个id是否相等,相等就直接进入,不相等就升级为轻量级锁;自旋一段时间后还没有获取到就升级到重量级锁。

synchronized、volatile、CAS区别

  • synchronized是悲观锁,属于抢占式,会引起其他线程阻塞
  • volatile提供多个线程共享变量可见性和禁止指令重排序
  • CAS是基于冲突监测的乐观锁(非阻塞)

synchronized、Lock、ReentrantLock区别

  • synchronized、ReentrantLock都是可重入锁
  • synchronized是关键字,Lock是接口,ReentrantLock是实现了Lock接口的一个类
  • synchronized可以给类、方法、代码块加锁,Lock和ReentrantLock只能给代码块加锁
  • synchronized不用手动获取和释放,发生异常会自动释放锁,不会造成死锁;Lock和ReentrantLock需要手动,没有unLock()就会死锁
  • Lock可以知道是否成功获取到锁,synchronized不行

6、volatile

保证可见性和禁止指令重排序,提供happens-before的保证,确保一个线程的修改对于其他线程是可见的。被volatile修饰的共享变量,当它被修改时,可以将修改的值立即更新到主内存中,其他线程需要读取时,重新去主内存中读取新值
volatile可以保证可见性和禁止重排序,但不能保证原子性;atomic方法可以让这种方法具有原子性

7、Lock体系

Lock是synchronized的扩展版本,Lock提供了无条件的、可轮询的(tryLock方法)、定时的(tryLock带参方法)、可中断的(lockInterruptibly)、可多条件队列的锁操作。Lock的实现基本都支持公平锁和非公平锁,synchronized只支持非公平锁

  • 悲观锁:悲伤的假设最坏的情况,每次拿数据都认为别人会修改,所以在拿的时候就会加锁,别人想拿就阻塞(共享资源每次只给一个线程使用,其他线程阻塞,用完再把资源转让给其他线程)
  • 乐观锁:每次拿数据,不会上锁,直到提交数据时才会证实数据是否被修改(产生并发冲突),多用于多读场景。一般用版本号或者CAS实现

8、CAS

CompareAndSweep——比较并交换
CAS包含三个操作数——内存位置(V)、预期值(A)、拟写入的新值(B)

  • 第一步:比较V和A是否相等
  • 第二步:相等,就把B写入V
  • 第三步:返回boolean类型,表示操作成功

多个线程进行CAS操作时,只有一个线程可以操作成功,其他线程自旋等待

CAS产生的问题

  • ABA问题:从A变到B,再从B变到A,过程不知道;解决办法:引入版本号
  • 循环开销时间大:资源竞争严重时,CAS自旋概率大,浪费CPU
  • 只能保证一个共享变量的原子性操作

9、线程死锁

两个或以上的线程互相持有对方资源并且不主动释放造成的恶性循环

死锁的四个条件

  • 互斥条件:一个资源只能被一个线程占用
  • 请求与保持条件:请求被占用资源而阻塞,不放弃已经获得的资源
  • 不剥夺条件:资源未使用前不能被其他线程强行剥夺
  • 循环等待条件:等待的线程形成了一个死循环

避免死锁

破坏造成死锁四个条件中的一个就行

  • 互斥条件无法破坏
  • 破坏请求与保持条件:一次性申请所有资源
  • 破坏不剥夺条件:申请不到被占用的资源,就主动释放
  • 破坏循坏等待条件:

活锁

没有被阻塞,只是某些条件没满足,导致一直重复尝试、失败、尝试、失败这个过程
活锁有可能自己解开,死锁不能

饥饿

因为种种原因无法获取到所需要的资源,导致一直无法执行

10、ThreadLocal

为线程提供局部变量,保证各个线程里的变量独立于其他线程的变量,也就是说ThreadLocal为每个线程创建一个单独的副本,线程之间不相关,
同步机制是为了保证多线程环境下数据的统一性,而ThreadLocal则是保证多线程环境下数据的独立性

ThreadLocal底层原理

Thread类中有一个ThreadHashMap的数据结构,用来保存线程对象的变量
每个线程的ThreadHashMap都是属于线程自己的,这就保证了每个线程都是独立的,多个操作不会互相影响

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

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

相关文章

前端vue 动态加载ts文件,动态调用ts内的方法

业务场景: 在某个业务场景中, 我们需要在数据库配置ts文件路径,和需要调用的函数名称, 前端需要再指定的场景下,触发对应的函数, 并执行处理逻辑,返回结果. 实现: 这是一个数据库配置生成的动态表单 动态校验的例子, 需要引用动态的函数校验 任意一个js文件, common1.ts c…

大模型日报|今日必读的 13 篇大模型论文

大家好,今日必读的大模型论文来啦! 1.MIT新研究:并非所有语言模型特征都是线性的 最近的研究提出了线性表征假说:语言模型通过操作激活空间中概念(“特征”)的一维表征来执行计算。与此相反,来…

CHI dataless 传输——CHI(4)

上篇介绍了read的操作类型,本篇我们来介绍一下dataless 目录 一、dataless操作概览 二、Non-CMO (Non-Cache Maintenance Operation) 1、CleanUnique 2、StashOnce and StashOnceSep 3、Evict 三、CMO (Cache Maintenance Operation) 一、dataless操作概览 名…

忍の摸头之术游戏娱乐源码

本资源提供给大家学习及参考研究借鉴美工之用,请勿用于商业和非法用途,无任何技术支持! 忍の摸头之术游戏娱乐源码,抖音上面非常火的摸头杀画面,看得我眼花缭乱,源码拿去玩吧; 目录说明 忍の摸头之术:域…

轻松同步:将照片从三星手机传输到iPad的简便方法

概括 想要在新 iPad 上查看三星照片吗?但是,如果您不知道如何将照片从三星手机传输到 iPad,则无法在 iPad 上查看图片。为此,本文分享了 7 个有用的方法,以便您可以使用它们在不同操作系统之间轻松发送照片。现在&…

EfficientSAM分割对象后求其中图像中的高

1 分割对象 EfficientSAM https://github.com/yformer/EfficientSAM 2 计算在图像中最高点即y值最小点 import os import cv2def read_images(folder_path):image_files [f for f in os.listdir(folder_path) iff.endswith(".jpg") or f.endswith(".png&quo…

虚拟化技术[1]之服务器虚拟化

文章目录 虚拟化技术简介数据中心虚拟化 服务器虚拟化服务器虚拟化层次寄居虚拟化裸机虚拟化VMM无法直接捕获特权指令解决方案 服务器虚拟化底层实现CPU虚拟化内存虚拟化I/O设备虚拟化 虚拟机迁移虚拟机动态迁移迁移内容:内存迁移迁移内容:网络资源迁移迁…

小短片创作-组装场景(一)

1、项目基础设置 通过第三人称模板,创建1个项目 1.自动曝光:关闭,因为要做专业的小短片,曝光需要手动控制。 2.扩展自动曝光中的默认亮度范围:启用 3.全局光照系统:选择屏幕空间光照(SSGI&am…

Transformer详解常见面试问题

文章目录 1. 各模块解决1.1 输入部分1.2 多头注意力(作者使用8个头)1.3 残差和LayerNorm1.4 Decoder部分 2.Transformer经典问题2.1 tranformer为何使用多头注意力机制?2.2 Transformer相比CNN的优缺点2.3 Encoder和decoder的区别&#xff1f…

Spring中RestTemplate用法

系列文章目录 文章目录 系列文章目录前言 前言 前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站,这篇文章男女通用,看懂了就去分享给你的码吧。 RestTemplate 是从…

自编译frida得一些记录

frida编译 这个过程坑肯定很多 但是只要大方向对得,解决掉每个小错误达到目的就ok得 # 就是想自己把frida代码done下来改一改 然后看看git clone gitgithub.com:frida/frida.git git fetch git checkout 14.1.3# 下载node包管理工具 apt install nvm nvm install …

Web Speech API(1)—— SpeechRecognition

Web Speech API 使你能够将语音数据合并到 Web 应用程序中。Web Speech API 有两个部分:SpeechSynthesis 语音合成(文本到语音 TTS)和 SpeechRecognition 语音识别(异步语音识别)。 SpeechRecognition 语音识别通过 S…

axios案例应用

1、Spring概述 Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层。Spring JDBC 以及业务层事务管理等众多…

day16|二叉树的属性

相关题目 ● 104.二叉树的最大深度 559.n叉树的最大深度 ● 111.二叉树的最小深度 ● 222.完全二叉树的节点个数 二叉树的深度与高度 如图, 二叉树的深度表示:任意一个叶子节点到根节点的距离,是从上往下计数的,因此使用前序遍历…

2024年甘肃特岗教师招聘报名流程,速速查收哦!

2024年甘肃特岗教师招聘报名流程,速速查收哦!

python-鸡兔同笼问题:已知鸡和兔的总头数与总脚数。求笼中鸡和兔各几只?

【问题描述】典型的鸡兔同笼问题。 【输入形式】输入总头数和总脚数两个实数:h,f 【输出形式】笼中鸡和兔的个数:x,y 【样例输入】16 40 【样例输出】鸡12只,兔4只 【样例说明】输入输出必须保证格式正确。…

AI大模型探索之路-训练篇25:ChatGLM3微调实战-基于LLaMA-Factory微调改造企业级知识库

系列篇章💥 AI大模型探索之路-训练篇1:大语言模型微调基础认知 AI大模型探索之路-训练篇2:大语言模型预训练基础认知 AI大模型探索之路-训练篇3:大语言模型全景解读 AI大模型探索之路-训练篇4:大语言模型训练数据集概…

CS西电高悦计网课设——校园网设计

校园网设计 一,需求分析 所有主机可以访问外网 主机可以通过域名访问Web服务器 为网络配置静态或者动态路由 图书馆主机通过DHCP自动获取IP参数 为办公楼划分VLAN 为所有设备分配合适的IP地址和子网掩码,IP地址的第二个字节使用学号的后两位。 二…

ESP32 实现获取天气情况

按照小安派AiPi-Eyes天气站思路,在ESP32 S3上实现获取天气情况。 一、在ESP32 S3实现 1、main.c 建立2个TASK void app_main(void) {//lvgl初始化xTaskCreate(guiTask, "guiTask", 1024 * 6, NULL, 5, NULL);//wifi初始化、socket、json处理taskcustom_…

ES6之数值的扩展

1. 数值的扩展 1.1. 二进制和八进制字面量表示:1.2. 数值判断方法: 1.2.1. Number.isFinite() 检查一个值是否为有限的数值。1.2.2. Number.isNaN() 更准确地检测NaN值。1.2.3. 传统的全局方法 isFinite() 和 isNaN() 的区别 1.3. 数值转换方法:1.4. 整数检查与精度: 1.4.1. Nu…