再次加深理解Java中的并发编程

目录

一、线程、进程、程序

二、线程状态

三、线程的七大参数

四、lock与synchronized锁机制

一)、lock与synchronized锁区别

二)、synchronized锁原理

三)、Lock锁原理

五、synchronized锁升级原理

一)、锁升级基础知识

二)、锁升级过程有什么用?

三)、synchronized锁升级具体过程

六、Volatile底层原理(可见性和禁止指令重排序)

一)线程安全三要素

二)volatile关键字是如何保证可见性和有序性呢?

三)volatile关键字是线程安全的吗?


一、线程、进程、程序

1.进程: 我们把运行中的程序叫做进程,每个进程都会占用内存与CPU资源,进程与进程之间互相独立,例如360杀毒软件中运行中

2.线程: 线程就是进程中的一个执行单元,负责当前进程中程序的执行。一个进程可以包含多个线程。多线程可以提高程序的并行运行效率。360杀毒软件中的垃圾清理功能

3.程序:是含有指令和数据的文件,被存储在磁盘或其他的数据存储设备中,也就是说程序是静态的代码,例如360杀毒软件

二、线程状态

1.新建(New):创建线程对象时

2.就绪(Runnable):线程调用start方法,有执行资格没有执行权

3.运行:当就绪状态时抢到cpu的执行权之后,进入运行状态

4.阻塞(Blocked):当获取锁失败后,进入阻塞状态

5.等待(Waiting):等待被notify()方法唤醒

6.休眠(sleep):休眠一段时间,时间到了之后,进入就绪状态。

7.终止(Terminated):线程死亡

注意1:调用start方法方可启动线程,而run方法只是thread类中的一个普通方法调用,还是在主线程里执行。

注意2:wait(long) 和 sleep(long) 的效果都是让当前线程暂时放弃 CPU 的使用权,进入阻塞状态

sleep是Thread的静态方法,而 wait()都是 Object 的成员方法,每个对象都有

sleep方法是暂停当前线程,相当于休眠一段时间,之后会自动唤醒,而wait()必须被notify 或者notifyall方法唤醒,不然会一直阻塞。

Wait会释放锁,sleep不会:wait方法的调用必须先获取wait对象的锁,而sleep则无此限制,wait方法执行后会释放对象锁,允许其它线程获得该对象锁(我放弃 cpu,但你们还可以用);但是如果在 synchronized代码块中执行,并不会释放对象锁(我放弃 cpu,你们也用不了),sleep方法执行后不会释放对象锁。

三、线程的七大参数

1.核心线程数(corePoolSize):表示保持活动状态的最小线程数。

2.最大线程数(maximumPoolSize):表示允许创建的最大线程数。

3.空闲时间(keepAliveTime):表示当线程数量超过核心线程数时,多余的空闲线程能够保持存活的时间。

4.阻塞队列(workQueue):表示用于存储等待执行的任务的阻塞队列。

5.单位(unit):表示空闲时间的单位,例如毫秒、秒等。

6.拒绝策略(rejectedExecutionHandler):表示当任务无法提交给线程池执行时采取的策略。

四、lock与synchronized锁机制

一)、lock与synchronized锁区别

1.语法层面:

1.1.synchronized是关键字,源码在jvm中,用c++ 语言实现

1.2.Lock 是接口,源码由jdk 提供,用java语言实现

2.释放锁机制:

        使用 synchronized 时,退出同步代码块锁会由jvm自动释放锁,比较被动而使用Lock时,需要手动调用unlock方法释放锁,否则可能会导致死锁等问题。

3.都支持可重入锁

        都可以用来控制多个线程访问共享资源的互斥性。都支持可重入锁机制,即同一个线程在已经获得锁的情况下能够再次获取该锁。

4.性能: 

        synchronized适用于简单线程同步场景,Lock适用于高并发场景下,拥有更好的性能和灵活性。

5.synchronized和lock如何保证线程安全?

        synchronized一般情况下通过修饰方法或代码块保证线程安全,而lock一般搭配unlock实现线程安全,lock和unlock中间放需要保证线程安全的代码。但为了避免lock死锁问题,一般将unlock放置于finally代码块中。


//控制方法
public synchronized void sync(){
}Object lock = new Object();
//控制代码块
public void sync(){
synchronized(lock){}Lock lock =new ReentrantLock();public void sync(){
lock.lock();//上锁
//TODO线程安全的代码
lock.unlock//释放锁,避免死锁}

二)、synchronized锁原理

        其主要原理是基于Java对象的内部锁,即监视器锁(Monitor Lock),确保在同一时刻只有一个线程可以访问被保护的代码块或方法。当一个线程尝试获取被synchronized关键字保护的资源时,如果该资源已被其他线程占用,该线程就会进入等待状态。占用资源的线程释放该资源时,等待队列中的线程会竞争获取该资源,并且只有一个线程会成功获取到该资源,其他线程继续等待。        

        synchronized关键字保证了可见性和原子性,可见性是通过JVM底层的内存屏障来实现的,原子性则是通过监视器锁的互斥性来实现的。在synchronized块内,线程获得了锁,它将会清空工作内存,从而使得该线程使用的变量能够从主内存中重新读取,同时也会把工作内存中的变量写回到主内存中。这样,其他线程就可以读取到最新的值,从而保证了可见性。

三)、Lock锁原理

        Lock的实现是基于Java的AbstractQueuedSynchronizer(AQS)框架的。Lock接口定义了多个获取和释放锁的方法,其中比较重要的是lock()和unlock()方法。当一个线程调用lock()方法获取锁时,如果锁未被占用,则该线程会占用锁并继续执行;否则,该线程会进入阻塞状态,直到锁被释放。当一个线程调用unlock()方法释放锁时,会通知等待队列中的其他线程继续尝试获取锁。

五、synchronized锁升级原理

一)、锁升级基础知识

1)偏向锁

        只有一个线程争抢锁资源的时候.将线程拥有者标识为当前线程。引入了偏向锁目的是来尽可能减少无竞争情况下的同步操作开销。当一个线程访问同步块并获取对象的锁时,会将锁的标记记录在线程的栈帧中,并将对象头中的Thread ID设置为当前线程的ID。此后,当这个线程再次请求相同对象的锁时,虚拟机会使用已经记录的锁标记,而不需要再次进入同步块。

2)轻量级锁(自旋锁)

        一个或多个线程通过CAS去争抢锁,如果抢不到则一直自旋。虚拟机会将对象的Mark Word复制到线程的栈帧中作为锁记录,并尝试使用CAS自旋操作尝试获取锁。如果CAS成功,则表示线程获取了轻量级锁,并继续执行同步块。如果CAS失败,说明有竞争,虚拟机会通过自旋等待其他线程释放锁。

3)重量级锁

        如果自旋等待不成功,虚拟机会将轻量级锁升级为重量级锁。在这种状态下,虚拟机会将线程阻塞,并使用操作系统的互斥量来实现锁的释放和获取。

        需要注意的是,锁的升级是逐级升级的过程,而不会存在降级。换句话说,一旦锁升级到更高级别,就不会再回到低级别。

二)、锁升级过程有什么用?

1)减少无竞争情况下的同步操作开销

        在多线程环境下,如果没有竞争,每个线程都可以安全地访问共享资源,无需进行同步操作。锁的升级过程中的第一阶段偏向锁(Biased Locking)就是为了在无竞争的情况下减少同步操作的开销。它通过记录线程ID来避免对锁的加锁和解锁操作,提高了单线程访问同步代码块时的性能

2)尽量避免线程切换的开销

        锁的升级过程中的第二阶段轻量级锁(Lightweight Locking)是为了减少线程切换的开销。它使用CAS操作来尝试获取锁,如果成功则可以继续执行同步块,无需线程切换;如果失败,则会进行自旋操作等待锁的释放。自旋操作避免了线程挂起和切换的开销,提高了多线程竞争时的性能

3)降低内存消耗

        锁的升级过程中的第二阶段轻量级锁使用对象头中的一部分位来存储线程ID和锁标记,不需要额外的内存存储锁的状态。相对于传统的重量级锁,它能够节省内存消耗。

4)提高系统吞吐量

        锁的升级过程可以使多个线程在无竞争情况下快速获取锁,避免了线程阻塞和等待的开销。这样,系统的吞吐量会更高,因为更多的线程可以并发地执行任务。

总而言之,锁的升级过程是为了提高多线程环境下的性能和吞吐量,减少同步操作的开销,并尽量避免线程切换的开销。Java虚拟机根据线程竞争的情况和锁的使用情况自动进行锁的升级和降级,以优化多线程程序的性能。

三)、synchronized锁升级具体过程

1)当只有一个线程去争抢锁的时候,会先使用偏向锁,就是给一个标识,说明现在这个锁被线程a占有。
2)后来又来了线程b,线程c,说凭什么你占有锁,需要公平的竞争,于是将标识去掉,也就是撤销偏向锁,升级为轻量级锁,三个线程通过CAS自旋进行锁的争抢(其实这个抢锁过程还是偏向于原来的持有偏向锁的线程).
3)现在线程a占有了锁,线程b,线程c一直在循环尝试获取锁,后来又来了十个线程,一直在自旋,那这样等着也是干耗费CPU资源,所以就将锁升级为重量级锁,向内核申请资源,直接将等待的线程进行阻塞。

六、Volatile底层原理(可见性和禁止指令重排序)

volatile 变量是一种比 sychronized 关键字更轻量级的同步机制。

一)线程安全三要素

原子性:一个操作或者多个操作,要么全部执行成功,要么全部执行失败。满足原子性的操作,中途不可被

中断。

可见性:多个线程共同访问共享变量时,某个线程修改了此变量,其他线程能立即看到修改后的值。

有序性程序执行的顺序按照代码的先后顺序执行。(由于JVM模型中允许编译器和处理器为了效率,进行指令重排序的优化。指令重排序在单线程内表现为串行语义,在多线程中会表现为无序。那么多线程并发编程中,就要考虑如何在多线程环境下可以允许部分指令重排,又要保证有序性)

二)volatile关键字是如何保证可见性和有序性呢?

保证可见性:当一个变量被volatile修饰后,JVM会把工作内存中的最新变量值强制刷新到主内存中,会导致其他线程中的本地缓存失效。这样,其他线程使用缓存时,发现本地工作内存中此变量失效,便会从主内存中获取,这样获取到的值就是最新的值,实现了线程之间可见性

保证有序性:通过编译器在生成字节码时,在指令序列中添加“内存屏障”来禁止指令重排序的,从而保证了有序性。(屏蔽在多线程环境下CPU的指令重排)

三)volatile关键字是线程安全的吗?

        因为volatile保证不了原子性,满足不了线程安全三要素,所以volatile不是线程安全的

值得说明的是,对 volatile 变量的单次读/写操作可以保证原子性的。如 long 和 double 类型变量,
但是并不能保证 i++这种操作的原子性,因为本质上 i++是读、写两次操作。在某些场景下可以代替 Synchronized。但是volatile 的不能完全取代 Synchronized 的位置,只有在一些特殊的场景下,才能适用 volatile。

总的来说,必须同时满足下面两个条件才能保证在并发环境的线程安全:
1、对变量的写操作不依赖于当前值(比如 i++),或者说是单纯的变量赋值(boolean 
flag = true)。
2、该变量没有包含在具有其他变量的不变式中,也就是说,不同的 volatile 变量之间,不
能互相依赖。只有在状态真正独立于程序内其他内容时才能使用 volatile。

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

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

相关文章

AIGC重塑金融:AI大模型驱动的金融变革与实践

🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​💫个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-tVrfBkGvUD0Qi13F {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

【微服务】配置Nacos管理SpringBoot配置文件(附解压包)

📝个人主页:哈__ 期待您的关注 一、什么是Nacos Nacos可以帮助我们配置和管理微服务,是阿里的一个开源产品,是针对微服务架构中的服务发现、配置管理、服务治理的综合型解决方案。Nacos可以用来实现配置中心和服务注册中心。 …

我国伺服系统市场规模逐渐扩大 未来有望实现完全国产替代

我国伺服系统市场规模逐渐扩大 未来有望实现完全国产替代 伺服系统又称为随动系统,是用于精确地跟随或复现某个过程的反馈控制系统。伺服系统主要包括驱动器、指令机构和电机等,可根据控制指令,对功率进行放大、变换与调控等处理,…

Jackson 2.x 系列【6】注解大全篇二

有道无术,术尚可求,有术无道,止于术。 本系列Jackson 版本 2.17.0 源码地址:https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 注解大全2.11 JsonValue2.12 JsonKey2.13 JsonAnySetter2.14 JsonAnyGetter2.15 …

QT 最近使用的项目配置文件

目录 1 QT 最近使用的项目配置文件所在路径 2 QtCreator.ini 1 QT 最近使用的项目配置文件所在路径 C:\Users\your username\AppData\Roaming\QtProject QtCreator.ini最好先备份一份 2 QtCreator.ini ProjectExplorer 下面的 RecentProjects\FileNames RecentProjects\…

Vue3:快速上手路由器

本人在B站上关于vue3的尚硅谷的课程,以下是整理一些笔记。 一.路由器和路由的概念 在 Vue 3 中,路由(Router)和路由器(Router)是两个相关但不同的概念。 1. 路由(Router)&#xff…

链表基础题

206. 反转链表 问题描述 给定单链表的头节点 head ,请反转链表,并返回反转后的链表的头节点。 示例 1: 输入:head [1,2,3,4,5] 输出:[5,4,3,2,1]示例 2: 输入:head [1,2] 输出:…

Cocos Creator 常见问题记录

目录 问题1、精灵图九宫格,角度不拉伸 问题2、BlockInputEvents 防止透屏 问题1、精灵图九宫格,角度不拉伸 点击编辑,拖拽到可变区域 问题2、BlockInputEvents 防止透屏

三菱GX WORKS3连接FX5U系列PLC时,弹出窗口提示:用户认证功能或安全性强化模式未启用

三菱GX WORKS3连接FX5U系列PLC时,弹出窗口提示:用户认证功能或安全性强化模式未启用 如下图所示,使用GX WORKS3编程软件连接FX5U系列PLC, 首先,在连接目标中选择自己当前使用的网卡适配器,并将IP地址设置在…

预训练大模型最佳Llama开源社区中文版Llama2

Llama中文社区率先完成了国内首个真正意义上的中文版Llama2-13B大模型,从模型底层实现了Llama2中文能力的大幅优化和提升。毋庸置疑,中文版Llama2一经发布将开启国内大模型新时代。 作为AI领域最强大的开源大模型,Llama2基于2万亿token数据预…

【pytest】执行环境切换的两种解决方案

一、痛点分析 在实际企业的项目中,自动化测试的代码往往需要在不同的环境中进行切换,比如多套测试环境、预上线环境、UAT环境、线上环境等等,并且在DevOps理念中,往往自动化都会与Jenkins进行CI/CD,不论是定时执行策略…

Leetcode 剑指 Offer II 071.按权重随机选择

题目难度: 中等 原题链接 今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~ 题目描述 给定一个正整数数组 w ,其中 w[i] 代表下标 i 的权重…

《AIGC重塑金融:AI大模型驱动的金融变革与实践》

🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​💫个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-oBSlqt4Vga1he7DL {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

聊聊k8s服务发现的优缺点

序 本文主要研究一下使用k8s服务发现的优缺点 spring cloud vs kubernetes 这里有张spring cloud与kubernetes的对比,如果将微服务部署到kubernetes上面,二者有不少功能是重复的,可否精简。 这里主要是讲述一下如果不使用独立的服务发现&am…

websocket 局域网 webrtc 一对一 视频通话的实例

基本介绍 使用websocket来 WebRTC 建立连接时的 数据的传递和交换。 WebRTC 建立连接时,通常需要按照以下顺序执行一些步骤: 1.创建本地 PeerConnection 对象:使用 RTCPeerConnection 构造函数创建本地的 PeerConnection 对象,该…

爬取b站音频和视频数据,未合成一个视频

一、首先找到含有音频和视频的url地址 打开一个视频,刷新后,找到这个包,里面有我们所需要的数据 访问这个数据包后,获取字符串数据,用正则提取,再转为json字符串方便提取。 二、获得标题和音频数据后&…

FreeRTOS day1

1.总结keil5下载代码和编译代码需要注意的事项 需要与板子连通 配置完成后才点击下载 2.总结STM32Cubemx的使用方法和需要注意的事项 下载支持包 打开芯片配置界面 3.总结STM32Cubemx配置GPIO的方法

【动手学深度学习-pytorch】9.2长短期记忆网络(LSTM)

长期以来,隐变量模型存在着长期信息保存和短期输入缺失的问题。 解决这一问题的最早方法之一是长短期存储器(long short-term memory,LSTM) (Hochreiter and Schmidhuber, 1997)。 它有许多与门控循环单元( 9.1节&…

目标检测评价标准

主要借鉴:https://github.com/rafaelpadilla/Object-Detection-Metrics?tabreadme-ov-file 主要评价指标、术语: Intersection Over Union (IOU):两个检测框交集面积与并集面积的比值 True Positive (TP):IOU大于阈值的检测框…

uniapp实现列表动态添加

1.效果图&#xff1a; 2.代码实现&#xff1a; 这里没有用uniapp提供的uni-list控件 <template> <view id"app"> <!-- 这里为了让标题&#xff08;h&#xff09;居中展示&#xff0c;给h标签设置了父标签&#xff0c;并设置父标签text-…