多线程与高并发- Synchronized锁

简介

synchronized 是 Java 语言的一个关键字,它允许多个线程同时访问共享的资源,以避免多线程编程中的竞争条件和死锁问题。synchronized可以用来给对象或者方法进行加锁,当对某个对象或者代码块加锁时,同时就只能有一个线程去执行。这种就是互斥关系,被加锁的区域称为临界区,而里面的资源就是临界资源。当一个线程进入临界区的时候,另一个线程就必须等待。
在这里插入图片描述
synchronized可以限制对某个资源的访问,但是它锁的并不是资源本身,可以锁住某个对象,只有线程拿到这把锁之后才能够去访问临界资源。如下代码,在我们想执行对count变量进行操作的时候,线程需要拿到o这个对象。

public class T1_Synchronized01 {private int count = 1;private Object o = new Object();public void m1() {synchronized (o) { // 必须先拿到o这个锁count++;System.out.println(Thread.currentThread().getName() + " count = " + count);}}
}

synchronized基础用法

1、通过对象进行锁

在代码里,可以通过创建一个对象,这样要想拿到临界资源,就必须先获得到这个对象的锁。

public class T1_Synchronized01 {private int count = 1;private Object o = new Object();public void m1() {synchronized (o) { // 必须先拿到o这个锁count++;System.out.println(Thread.currentThread().getName() + " count = " + count);}}
}
2、通过this

使用this代表锁住的是当前对象,这种方法等同直接把synchronized关键字加在方法前。

public class T1_Synchronized01 {private int count = 1;public void m2() {synchronized (this) { // 必须先拿到this的锁count++;System.out.println(Thread.currentThread().getName() + " count = " + count);}}public synchronized void m3() { // 与m2一样count++;System.out.println(Thread.currentThread().getName() + " count = " + count);}}
3、锁定静态方法

锁定静态方法需要通过类.class,或者直接在静态方法上加上关键字。但是,类.class不能使用this来代替。注:在同一个类加载器中,class是单例的,这也就能保证synchronized能够只让一个线程访问临界资源。

public class T1_Synchronized01 {public static void m4() { // 静态方法synchronized (T1_Synchronized01.class) {System.out.println(Thread.currentThread().getName());}}public synchronized static void m5() {System.out.println(Thread.currentThread().getName());}
}
4、实验测试

①、首先测试一下,同步和非同步是否可以相互调用
定义两个线程,一个执行同步方法,一个执行非同步方法,如果不能够互相调用,那么,非同步线程就需要等待同步线程执行完之后在继续执行。

public class T3_Synchronized03 {public synchronized void one() {System.out.println(Thread.currentThread().getName() + " start one method");try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println(Thread.currentThread().getName() + " end one method");}public void two() {System.out.println(Thread.currentThread().getName() + " execute two method");}public static void main(String[] args) {T3_Synchronized03 t = new T3_Synchronized03();new Thread(t::one, "第一个线程").start();new Thread(t::two, "第二个线程").start();}
}

从运行的结果可以看出是可以的。
在这里插入图片描述
②、读写不全加锁会怎样
通过购票与查询票数来进行模拟读写加锁问题。
首先,看以下代码是给读写都进行加锁了,在扣掉票数的时候,休眠了2秒,当线程执行了购票之后,通过多个线程去查询票数,每次启动线程会睡眠0.5秒。

public class T4_Synchronized {private int ticket = 100; // 模拟100张票public synchronized int getTicket() { // 读return this.ticket;}public synchronized void buy(int number) { // 写try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}ticket = ticket - number;}public static void main(String[] args) throws InterruptedException {T4_Synchronized bus = new T4_Synchronized();System.out.println("刚开始有票数:" + bus.getTicket());new Thread(() -> bus.buy(1)).start();for (int i = 1; i <= 10; i++) {Thread.sleep(500);int finalI = i;new Thread(() -> System.out.println("第" + finalI + "次查询余票数:" + bus.getTicket())).start();}}
}

运行之后,我们可以发现,数据是正确的,尽管是在查询的时候并没有睡眠0.5秒,显示数据依然是期望数据。
在这里插入图片描述
然而,当我们把读的锁去掉,运行代码,会发现,数据读出来了脏数据,为了更好的显示,查询票数的睡眠不要去掉。
在这里插入图片描述
③、synchronized的可重入性
定义一个类,类中有两个同步方法,他们锁的对象都是当前类,如果不能够重入,在one线程启动的时候就会死锁。在同步方法one中去调用同步方法two,当线程启动的时候,已经获取了对象的锁,等调用two方法的时候,同样是拿到了这个对象的锁。所以synchronized是可重入的。

public class T5_Synchronized {synchronized void one() {try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}two();System.out.println("one - thread-" + Thread.currentThread().getName() + " end");}synchronized void two() {try {Thread.sleep(2000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("two - thread-" + Thread.currentThread().getName() + " end");}public static void main(String[] args) {T5_Synchronized t5 = new T5_Synchronized();new Thread(t5::one, "one1").start();new Thread(t5::one, "one2").start();new Thread(t5::one, "one3").start();}
}

实验结果
在这里插入图片描述
④、异常会释放锁
当线程执行过程中出现了异常,synchronized的锁会被释放,这样其他需要访问这个临界资源的线程就能进入执行。

public class T6_Synchronized {int count = 0;synchronized void add() {System.out.println("线程 " + Thread.currentThread().getName() + " start");while (true) {count++;System.out.println("线程 " + Thread.currentThread().getName() + " now count = " + count);if (count == 3) {throw new NullPointerException("人为异常");}if (count == 10) {throw new NullPointerException("测试结束");}}}public static void main(String[] args) throws InterruptedException {T6_Synchronized t = new T6_Synchronized();new Thread(t::add, "1").start();Thread.sleep(1000);new Thread(t::add, "2").start();}
}

当第一次异常抛出时,线程2就立即进入执行。
在这里插入图片描述

synchronized锁的底层原理

synchronized实现锁的基础就是Java对象头,synchronized锁会将线程ID存入mark word(对象头由标记字)。关于mark word,先简要了解一下Java对象。
在Hotspot 虚拟机中,对象在内存中的存储布局,可以分为三个区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。synchronized主要是跟对象头有关系,在对象头中包含了标记字(mark word)、类指针(klass word)和 数组长度(array length)。也就是通过mark word的字节位数来表示各种锁状态。
在这里插入图片描述
synchronized锁在线程第一次访问的时候,实际上是没有加锁的,只是在mark word中记录了线程ID,这种就是偏向锁,默认是认为不会有多个线程抢着用,mark word是通过64bit来表示的,通过最低2位也就是锁标志位,偏向锁与无锁的值是01,轻量级锁用00表示,重量级锁用10表示,标记了GC的用11表示,无锁与偏向锁低2位是一致的,在倒数第3位有1位来表示偏向锁位:值为1表示偏向锁。
在这里插入图片描述

这里引用一张掘金博客上的图:https://juejin.cn/post/6978882583492821023

synchronized锁升级

● synchronized锁在线程第一次访问的时候,实际上是没有加锁的,只是在mark word中记录了线程ID,默认也就是使用偏向锁。
● 当第二个线程来争用的时候,此时第二个线程会占用cpu,循环等待锁的释放,这时候偏向锁也就升级为自旋锁。
● 当自旋10次之后,就会升级为重量级锁,重量级锁是不占用cpu,他是使用OS的。
当线程数较少、运行时间较短的时候是比较适合使用自旋锁,反之则比较适合重量级锁。


---------------------
作者:一个有梦有戏的人
来源:CSDN
原文:https://blog.csdn.net/qq_43843951/article/details/129107202
版权声明:本文为作者原创文章,转载请附上博文链接!
内容解析By:CSDN,CNBLOG博客文章一键转载插件

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

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

相关文章

界面组件Kendo UI for Angular 2024 Q2亮点 - 让应用程序界面拥有AI提示

随着最新的2024年第二季度发布&#xff0c;Kendo UI for Angular为应用程序开发设定了标准&#xff0c;包括生成式AI集成、增强的设计系统功能和可访问的数据可视化。新的2024年第二季度版本为应用程序界面提供了人工智能(AI)提示&#xff0c;从设计到代码的生产力增强、可访问…

【分数限制下,专科学子将何去何从】

本文仅讲述专科学子 高考之前&#xff0c;咱就是班里中等偏上水平&#xff0c;有时考试考的好点能冲上历年本科线&#xff0c;有时考的差点就直接名落孙山。 现在高考完了&#xff0c;也就可能在本科线周围晃荡&#xff0c;那我接下来该怎么办呢。 是继续上学&#xff0c;还是去…

奇怪的缓存一致性问题

天猫国际用户Push中心承接了国际用户触达相关的需求&#xff0c;比如短信、端内消息投放等等&#xff0c;并存在较高的并发场景。 该系统此前发现了一个查询投放计划plan为null的异常情况&#xff0c;在初期排查时有些丈二和尚摸不着头脑&#xff0c;后面突然灵光乍现——原来是…

Django从入门到精通:First [Django版本.Python面向对象.Web基础.创建Django项目]

文章目录 Django初学者指南1 Django简介1.1 Django的历史1.2 使用Django的知名网站1.4 Django的主要特点1.5 Django的工作原理 2 Django 版本选择2.1 Django 支持的 Python 版本2.2 Django 版本 3 Django 开发 Web 程序3.1 Python知识点3.1.1 Python 函数3.1.2 Python 面向对象…

智能鼠类监测设备

TH-SH1在人类社会与自然环境共存的过程中&#xff0c;鼠类作为一类常见的害虫&#xff0c;给我们的生活、生产带来了诸多不便。为了更好地控制鼠害&#xff0c;科学家们不断研发出各种高效的监测与防控设备。近期&#xff0c;一款全新的鼠类监测设备崭露头角&#xff0c;其能够…

python基础语法学习(工程向)-Stage3-数据可视化

json 是一种轻量的数据交互格式&#xff0c;可以按照json指定的格式去组织和封装数据&#xff0c;而本质上是一个带有特定格式的字符串。 功能 json是在各个编程语言中流通的数据格式&#xff0c;负责不同编程语言之间的数据传递和交互。 格式 json的格式要求较为严格&#…

九、数据结构(并查集)

文章目录 1.并查集操作的简单实现2.解决问题3. 并查集优化3.1 合并的优化3.2查询优化3.3查询优化2 通常用“帮派”的例子来说明并查集的应用背景&#xff1a;在一个城市中有 n ( n < 1 0 6 ) n(n < 10^6) n(n<106)个人&#xff0c;他们分成不同的帮派&#xff0c;给出…

华为HCIP Datacom H12-821 卷6

1.单选题 下面是一台路由器的部分配置&#xff0c;关于该部分配置描述正确的是&#xff0c;[HUAWEllJip ip-prefix plpermit 10.0.192.0 8 greater-equal 17 less-equal 18 A、10.0.192.0/8 网段内&#xff0c;掩码长度为 20 的路由会匹配到该前缀列表&#xff0c;匹配规则为…

餐饮点餐系统小程序(ThinkPHP+FastAdmin+UniApp)

便捷美食新体验&#x1f354;&#x1f4f1; 基于ThinkPHPFastAdminUniApp开发的餐饮点餐系统&#xff0c;主要应用于餐饮&#xff0c;例如早餐、面馆、快餐、零食小吃等快捷扫码点餐需求&#xff0c;标准版本仅支持先付款后就餐模式&#xff0c;高级版本支持先付后就餐和先就餐…

C语言---自定义类型:结构体

结构体回顾 结构体 自定义的类型&#xff1a;结构体、联合体、枚举 结构是一些值的集合&#xff0c;这些值成为成员变量&#xff0c;结构的每个成员可以是不同类型的变量 //描述一本书&#xff1a;书名、作者、定价、书号//结构体类型---类似于整型、浮点型 struct Book {c…

使用 Vue CLI 脚手架生成 Vue 项目

最近我参与了一个前端Vue2的项目。尽管之前也有过参与Vue2项目的经验&#xff0c;但对一些前端Web技术并不十分熟悉。这次在项目中遇到了很多问题&#xff0c;所以我决定借此机会深入学习Vue相关的技术栈。然而&#xff0c;直接开始深入钻研这些技术可能会显得枯燥&#xff0c;…

AI工具快速制作爆火的影视视频混剪

今天给大家发一个有意思的工具&#xff0c;影视混剪大家应该都刷到过&#xff0c;像下面这种视频&#xff0c;播放量都超级高。 这种视频都是怎么做的呢&#xff1f; 现在AI工具这么多样性&#xff0c;先用 AI 写一段具有网感的对话段子&#xff0c;然后找影视剧片段混剪成一…

【Vue】封装组件的过程

文章目录 方式一方式二 首先&#xff0c;组件可以提升整个项目的开发效率。能够把页面抽象成多个相对独立的模块&#xff0c;解决了我们传统项目开发&#xff1a;效率低、难维护、复用性等问题。 方式一 Vue封装组件的过程通常包括以下几个步骤&#xff0c;这些步骤旨在创建可…

反激开关电源压敏电阻选型及计算

作用:在浪涌时间里面吸收一个很高的浪涌电压 压敏电阻对电压敏感&#xff0c;有变阻电压阈值 在电压阈值以下&#xff0c;表现出阻抗很大&#xff0c;超过电压阈值&#xff0c;表现出阻抗很小 压敏的选型及计算 压敏电阻的作用是抑制来自电源在异常时的尖峰电压和瞬态过电压&a…

四川音盛佳云电子商务有限公司引领商业新潮流

在当今这个数字化飞速发展的时代&#xff0c;电商行业正以其独特的魅力吸引着越来越多的目光。而在众多电商企业中&#xff0c;四川音盛佳云电子商务有限公司凭借其专业、专注的抖音电商服务&#xff0c;逐渐崭露头角&#xff0c;成为行业的佼佼者。 四川音盛佳云电子商务有限…

elasticsearch hanlp插件远程词典配置

elasticsearch hanlp插件远程词典配置 背景远程词典配置新增远程词典文件修改hanlp-remote.xml自动加载词典 远程词典测试 背景 在使用elasticsearch的过程中&#xff0c;总会遇到与分词相关的需求&#xff0c;这里将针对常用的elasticsearch hanlp&#xff08;后面统称为 es …

gradio快速入门 — Interface状态

目录 全局状态会话状态 到目前为止&#xff0c;我们假设您的演示是无状态的&#xff1a;它们不会保留单个函数调用以外的信息。如果您想根据与演示的先前交互来修改演示的行为&#xff0c;该怎么办&#xff1f; Gradio 中有两种方法&#xff1a;全局状态和会话状态。 全局状态…

【Docker实战】jenkins卡在编译Dockerfile的问题

我们的项目是标准的CI/CD流程&#xff0c;也即是GitlabJenkinsHarborDocker的容器自动化部署。 经历了上上周的docker灾难&#xff0c;上周的服务器磁盘空间灾难&#xff0c;这次又发生了jenkins卡住的灾难。 当然&#xff0c;这些灾难有一定的连锁反应&#xff0c;是先发生的d…

简单通用防篡改水印组件封装(vue3)

一、项目结构 二、项目代码 1.App.vue <template><div class"container"><Watermark text"版权所有"><div class"content"></div></Watermark><Watermark text"禁止转载" style"backgr…

数据资产赋能智能决策:通过深度挖掘数据资产价值,构建全面智能决策支持系统,精准分析,辅助决策,显著提升企业决策质量与效率,推动业务快速发展

一、引言 在信息化和数字化飞速发展的今天&#xff0c;数据已成为企业最宝贵的资产之一。数据资产不仅记录着企业的历史运营轨迹&#xff0c;更蕴含着企业未来发展的无限可能。然而&#xff0c;如何深度挖掘数据资产的价值&#xff0c;将其转化为推动企业发展的动力&#xff0…