深入学习锁--Synchronized各种使用方法
一、什么是synchronized
在Java当中synchronized通常是用来标记一个方法或者代码块。在Java当中被synchronized标记的代码或者方法在同一个时刻只能够有一个线程执行被synchronized修饰的方法或者代码块。因此被synchronized修饰的方法或者代码块不会出现数据竞争的情况,也就是说被synchronized修饰的代码块是并发安全的。synchronized是java内置关键字,是内置锁,JVM中内置了,颗粒度比较大
二、synchronized的四种用法
2.1、修饰一个代码块
被修饰的代码块称为同步语句块,其作用的范围是大括号{} 括起来的代码,作用的对象是调用这个代码块的对象;
2.2、修饰一个方法
被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
非静态方法使用 synchronized 修饰的写法,修饰实例方法时,锁定的是当前实例对象:
2.3、修饰一个静态的方法
其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
2.4、修饰一个类
其作用的范围是synchronized后面括号括起来的部分,作用对象是这个类的所有对象。
三、使用案例分析
3.1、修饰一个方法
class SyncDemo {private int count;public void add() {count++;}public static void main(String[] args) throws InterruptedException {SyncDemo syncDemo = new SyncDemo();Thread t1 = new Thread(()->{for (int i = 0; i <1000 ; i++) {syncDemo.add();}});Thread t2 = new Thread(()->{for (int i = 0; i <1000 ; i++) {syncDemo.add();}});t1.start();t2.start();t1.join(); //线程的阻塞方法,线程t1执行完毕,再执行main主线程打印语句t2.join(); //线程的阻塞方法,线程t2执行完毕,再执行main主线程打印语句System.out.println(syncDemo.count);}
}
由于add
方法没有使用synchronized
修饰,线程t1和线程t2可能同时去执行add
方法,那么就会导致最终count
的结果小于20000,因为count++
操作不具备原子性。
将上述add方法被synchronized修饰
public synchronized void add() {count++;}
由于add方法被
synchronized
修饰,因此每一个时刻只能有一个线程执行add
方法,因此上面打印的结果是20000
总结:
synchronized
修饰的add
方法一个时刻只能有一个线程执行的意思是对于一个SyncDemo
类的对象来说一个时刻只能有一个线程进入。比如现在有两个SyncDemo
的对象s1
和s2
,一个时刻只能有一个线程进行s1
的add
方法,一个时刻只能有一个线程进入s2
的add
方法,但是同一个时刻可以有两个不同的线程执行s1
和s2
的add
方法,也就说s1
的add
方法和s2
的add
是没有关系的,一个线程进入s1
的add
方法并不会阻止另外的线程进入s2
的add
方法,也就是说synchronized
在修饰一个非静态方法的时候“锁”住的只是一个实例对象,并不会“锁”住其它的对象。其实这也很容易理解,一个实例对象是一个独立的个体别的对象不会影响他,他也不会影响别的对象。
3.2、修饰一个静态的方法
class SyncDemo {private static int count; //静态变量public static synchronized void add() { //静态方法count++;}public static void main(String[] args) throws InterruptedException {SyncDemo syncDemo = new SyncDemo();Thread t1 = new Thread(()->{for (int i = 0; i <1000 ; i++) {syncDemo.add();}});Thread t2 = new Thread(()->{for (int i = 0; i <1000 ; i++) {syncDemo.add();}});t1.start();t2.start();t1.join(); //线程的阻塞方法,线程t1执行完毕,再执行main主线程打印语句t2.join(); //线程的阻塞方法,线程t2执行完毕,再执行main主线程打印语句System.out.println(syncDemo.count); //输出结果为2000}
}
上面的代码最终输出的结果也是20000,但是与前一个程序不同的是。这里的
add
方法用static
修饰的,在这种情况下真正只能有一个线程进入到add方法
,因为用static
修饰的add方法是静态方法,静态方法所有对象公共的方法,因此和前面的那种情况不同,不存在两个不同的线程同一时刻执行不同实例对象的add
方法。你仔细想想如果能够让两个不同的线程执行
add
代码块,那么count++
的执行就不是原子的了。那为什么没有用static
修饰的代码为什么可以呢?因为当没有用static
修饰时,每一个对象的count
都是不同的,内存地址不一样,因此在这种情况下count++
这个操作仍然是原子的!
3.3、修饰一个代码块
class SyncDemo {private int count; //静态变量public void add() {System.out.println("进入了add方法");synchronized (this){count++;}}public void minus() {System.out.println("进入了minus方法");synchronized (this){count--;}}public static void main(String[] args) throws InterruptedException {SyncDemo syncDemo = new SyncDemo();Thread t1 = new Thread(()->{for (int i = 0; i <10 ; i++) {syncDemo.add();}});Thread t2 = new Thread(()->{for (int i = 0; i <10 ; i++) {syncDemo.minus();}});t1.start();t2.start();t1.join(); //线程的阻塞方法,线程t1执行完毕,再执行main主线程打印语句t2.join(); //线程的阻塞方法,线程t2执行完毕,再执行main主线程打印语句System.out.println(syncDemo.count); //输出结果为0}
}
有时候我们并不需要用
synchronized
去修饰一个方法,因为这样并发度就比较低了,一个方法一个时刻只能有一个线程在执行。因此我们可以选择用synchronized
去修饰代码块,只让某个代码块一个时刻只能有一个线程执行,除了这个代码块之外的代码还是可以并行的。比如上面的代码当中
add
和minus
方法没有使用synchronized
进行修饰,因此一个时刻可以有多个线程执行这个两个方法。在上面的synchronized
代码块当中我们使用了this
对象作为锁对象,只有拿到这个锁对象的线程才能够进入代码块执行,而在同一个时刻只能有一个线程能够获得锁对象。也就是说add
函数和minus
函数用synchronized
修饰的两个代码块同一个时刻只能有一个代码块的代码能够被一个线程执行,因此上面的结果同样是0。这里说的锁对象是
this
也就SyncDemo
类的一个实例对象,因为它锁住的是一个实例对象,因此当实例对象不一样的时候他们之间是没有关系的,也就是说不同实例用synchronized
修饰的代码块是没有关系的,他们之间是可以并发的。
3.4、修饰一个类
class SyncDemo {private int count; //静态变量public void add() {System.out.println("进入了add方法");synchronized (SyncDemo.class){count++;}}public void minus() {System.out.println("进入了minus方法");synchronized (SyncDemo.class){count--;}}public static void main(String[] args) throws InterruptedException {SyncDemo syncDemo = new SyncDemo();Thread t1 = new Thread(()->{for (int i = 0; i <10 ; i++) {syncDemo.add();}});Thread t2 = new Thread(()->{for (int i = 0; i <10 ; i++) {syncDemo.minus();}});t1.start();t2.start();t1.join(); //线程的阻塞方法,线程t1执行完毕,再执行main主线程打印语句t2.join(); //线程的阻塞方法,线程t2执行完毕,再执行main主线程打印语句System.out.println(syncDemo.count); //输出结果为0}
}
上面的代码是使用
synchronized
修饰类,锁对象是SyncDemo.class,这个时候他不再是锁住一个对象了,而是一个类了,这个时候的并发度就变小了,上一份代码当锁对象是SyncDemo的实例对象时并发度更大一些,因为当锁对象是实例对象的时候,只有实例对象内部是不能够并发的,实例之间是可以并发的。但是当锁对象是SyncDemo.class的时候,实例对象之间时不能够并发的,因为这个时候的锁对象是一个类。
四、Synchronized与可见性和重排序
4.1互斥性/排他性(非公平锁)
synchronized(非公平锁)会起到互斥效果,某个线程执⾏到某个对象的 synchronized 中时,其他线程如果也执⾏到同⼀个对象 synchronized 就会阻塞等待。
- 进⼊ synchronized 修饰的代码块, 相当于加锁。
- 退出 synchronized 修饰的代码块, 相当于解锁。
PS:公平锁 VS 非公平锁
公平锁:按资排辈,先到先得。需要“唤醒”这一步操作,会牺牲一定的性能。(线程发现锁占用,尝试获取锁一段时间后,进入休眠状态,进入排队队列中等待。上一个线程释放锁后,会唤醒排队等候的其他线程中最前面的线程从阻塞状态又切换至运行状态)
非公平锁:来得早不如来得巧,不需要“唤醒”,性能高。(线程1发现锁占用,尝试获取锁一段时间后,进入休眠状态。此时线程2来了,处于运行状态,尝试获取锁,此时刚好上一个线程释放了锁,那么线程2直接得到了锁并去运行它的任务了。排队等待的其他线程获取锁的顺序不是按照访问的顺序先来先到的,而是由线程自己竞争,随机获取到锁)Java里所有的锁默认是非公平锁。
4.2可见性
-
当一个线程进入到
synchronized
同步代码块的时候,将会刷新所有对该线程的可见的变量,也就是说如果其他线程修改了某个变量,而且线程需要在Synchronized
代码块当中使用,那就会重新刷新这个变量到内存当中,保证这个变量对于执行同步代码块的线程是可见的。 -
当一个线程从同步代码块退出的时候,也会将线程的工作内存同步到内存当中,保证在同步代码块当中修改的变量对其他线程可见。
4.3可重入性(可重入性锁)
synchronized 同步块对同⼀条线程来说是可重⼊的,不会出现⾃⼰把⾃⼰锁死的问题。
/*** synchronized 可重入性测试*/
public class ThreadSynchronized4 {public static void main(String[] args) {synchronized (ThreadSynchronized4.class) {System.out.println("当前主线程已经得到了锁"); //当执行到此行代码时,表示已经获得锁synchronized (ThreadSynchronized4.class) { //同一个线程获取锁两次System.out.println("当前主线程再次得到了锁"); //若两行代码都能打印,说明具备可重入性}}}
}
五、synchronized实现原理
(面试必问)synchronized是如何实现的?
①在Java代码层面:synchronized加锁的对象里有一个的隐藏的对象头,这个对象头(可看作一个类)里有很多属性,其中比较关注的两个属性是:是否加锁的标识和拥有当前锁的线程id。
每次进⼊ synchronized 修饰的代码块时,会去对象头中先判断加锁的标识,再判断拥有当前锁的线程id,从而决定当前线程能否往下继续执行。
判断加锁标识为false->对象头未加锁,当前线程可以进入synchronized 修饰的代码块,并设置加锁标识为true,设置拥有当前锁的线程id为此线程id。
判断加锁标识为true->对象头已加锁,需进一步判断拥有当前锁的线程id是否为此线程id,若是,则继续往下执行;否则,不能往下执行,需要等待锁资源释放后重新竞争再获取锁。
②在JVM层面和操作系统层面:synchronized同步锁是通过JVM内置的Monitor监视器实现的,而监视器又是依赖操作系统的互斥锁Mutex实现的。↓
————————————————
原文链接:https://blog.csdn.net/WWXDwrn/article/details/129115774
六、synchronized历史发展进程
- 在JDK1.6之前(多使用Lock)synchronized使用很少,那时synchronized默认使用重量级锁实现,所以性能较差。
- 在JDK1.6时,synchronized做了优化。锁升级流程如下:
1,无锁:没有线程访问时默认是无锁状态,加了synchronized也是无锁状态。有线程访问时才加锁。更大程度上减少锁带来的程序上的开销。
2,偏向锁:当有一个线程访问时会升级为偏向锁。(在对象头里存了这样一把锁,后面再来线程时会判断,如果线程id和拥有锁的线程id相同,会让它进去,只偏向某一个线程,其他线程来了之后要继续等)
3,轻量级锁:当有多个线程访问时会升级为轻量级锁。
4,重量级锁:当大量线程访问同时竞争锁资源的时候会升级为重量级锁。
原文链接:https://baijiahao.baidu.com/s?id=1740505389877266267&wfr=spider&for=pc
相关文章:
深入学习锁--Synchronized各种使用方法
一、什么是synchronized 在Java当中synchronized通常是用来标记一个方法或者代码块。在Java当中被synchronized标记的代码或者方法在同一个时刻只能够有一个线程执行被synchronized修饰的方法或者代码块。因此被synchronized修饰的方法或者代码块不会出现数据竞争的情况&#x…...
pycharm中绘制一个3D曲线
import numpy as np import matplotlib.pyplot as plt # 中文的设置 import matplotlib as mp1 from mpl_toolkits.mplot3d import Axes3D mp1.rcParams["font.sans-serif"] ["kaiti"] mp1.rcParams["axes.unicode_minus"] False # 数据创建 X…...
人工智能_AI服务器安装清华开源_CHATGLM大语言模型_GLM-6B安装部署_人工智能工作笔记0092
看到的这个开源的大模型,很牛,~关键让我们自己也可以部署体验一把了,虽然不知道具体内部怎么构造的但是,也可以自己使用也挺好. 可以部署在自己的机器上也可以部署在云服务器上. 安装以后,是可以使用python代码进行提问,然后返回结果的,这样就可以实现我们自己的chat应用了, …...
用户反馈组件实现(Vue3+ElementPlus)含图片拖拽上传
用户反馈组件实现(Vue3ElementPlus)含图片拖拽上传 1. 页面效果1.1 正常展示1.2 鼠标悬浮1.3 表单 2. 代码部分1.2 html、ts1.2 less部分 3. 编码过程遇到的问题 1. 页面效果 1.1 正常展示 1.2 鼠标悬浮 1.3 表单 2. 代码部分 1.2 html、ts <templ…...
K8S部署nginx并且使用NFS存储数据
安装NFS 在master安装NFS systemctl start nfs-server修改配置 /etc/exports /data *(rw,no_root_squash,no_all_squash,sync)目录为 /data 允许所有地址访问 验证下 [rootmaster nginx]# showmount -e 192.168.57.61 Export list for 192.168.57.61: /data *共享可以正常…...
Homework 3: Higher-Order Functions, Self Reference, Recursion, Tree Recursion
Q1: Compose 编写一个高阶函数composer,它返回两个函数func和func_adder。 func是一个单参数函数,它应用到目前为止已经组合的所有函数。这些函数将首先应用最新的函数(参见doctests和示例)。 func_adder用于向我们的组合添加更多…...
(C++)有效三角形的个数--双指针法
个人主页:Lei宝啊 愿所有美好如期而遇 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://le…...
11.30BST理解,AVL树操作,定义;快速幂,二分求矩阵幂(未完)
完全二叉树结点的度可能有1,满二叉树的度只能为0或2 BST构建 BST是左孩子都比根节点小,右孩子都比根节点大 二叉搜索树的插入,删除,调整 平衡树理解 任何一个平衡二叉树,它的中序遍历都是一样的,都是有…...
深入理解Java核心技术:Java工程师的实用干货笔记
💂 个人网站:【 海拥】【神级代码资源网站】【办公神器】🤟 基于Web端打造的:👉轻量化工具创作平台💅 想寻找共同学习交流的小伙伴,请点击【全栈技术交流群】 在Java工程师的职业生涯中,深入理解…...
大学里面转专业介绍
目录 个人情况转专业过程中的经验分享转专业后的学习建议和心态调整转专业后的时间平衡 个人情况 信息科学与工程学院计算机科学与技术专业2019级本科生,曾从物理与微电子科学学院后转入信息科学与技术学院。学习成绩连续三年专业前10% 项目:爬虫项目、…...
MySQL_1. mysql数据库介绍
shell脚本差不多快完结了接下来会为大家更新MySQL系列的相关的基础知识笔记,希望对大家有所帮助,好废话不多说,接下来开始正题! 1.mysql数据库介绍 mysql 是一款安全、跨平台、高效的,并与 PHP、Java 等主流编程语言…...
TimeGPT:时间序列预测模型实例
时间序列预测领域正在经历一个非常激动人心的时期。在过去的三年里,我们见证了许多重要的贡献,如N-BEATS、N-HiTS、PatchTST和TimesNet等。同时,大型语言模型(LLM)近来在流行度方面取得了很大的成功,例如Ch…...
【JavaEE】多线程 (1)
目录 1. 认识线程(Thread) 1) 线程是什么 2) 为啥要有线程 3) 进程和线程的区别 2.第⼀个多线程程序 3.多线程的其他创建方式 方法二:实现 Runnable 接⼝ 方法三:匿名内部类 方法四:实现Runable, 重写run, 匿名内部类 方法五:使用lambda表达式…...
linux 应用层同步与互斥机制之条件变量
2、条件变量 互斥量防止多个线程同时访问同一共享变量。(我们称为互斥) 有一种情况,多个线程协同工作。一个线程的消费需要等待另一个线程的产出。必须线程B完成了应有的任务,满足了某一个条件,线程A才能继续执行。&…...
3.5毫米音频连接器接线方式
3.5毫米音频连接器接线方式 耳机插头麦克风插头 绘制电路图注意事项 3.5毫米音频连接器分为单声道开关型和无开关型如下图: sleeve(套筒) tip(尖端) ring(环) 耳机插头 麦克风插头 绘制电路图…...
智慧农田可视化大数据综合管理平台方案,EasyCVR助力农业高质量发展
一、背景需求 我国是农业大国,农业耕地面积达到20亿亩。随着物联网、大数据、人工智能等新一代信息技术与农业农村加速融合,以及国家对农业的重视,智慧农业对于我国农业现代化建设和实施乡村振兴战略具有重大引领与推动作用。在传统农田生产…...
python超详细基础文件操作【建议收藏】
文章目录 前言1 文件操作1.1 文件打开与关闭1.1.1 打开文件1.1.2 关闭文件 1.2 访问模式及说明 2 文件读写2.1 写数据(write)2.2 读数据(read)2.3 读数据(readlines)2.3 读数据(readline&#x…...
华为变革进展指数TPM的五个级别:试点级、推行级、功能级、集成级和世界级
华为变革进展指数TPM的五个级别:试点级、推行级、功能级、集成级和世界级 TPM(Transformation Progress Metrics,变革进展指标)用来衡量管理体系在华为的推行程度和推行效果,并找出推行方面的不足与问题,…...
vue el-select多选封装及使用
使用了Element UI库中的el-select和el-option组件来构建多选下拉框。同时,也包含了一个el-input组件用于过滤搜索选择项,以及el-checkbox-group和el-checkbox组件用于显示多选项。 创建组件index.vue (src/common-ui/selectMultiple/index.vue) <tem…...
大模型上下文学习(ICL)训练和推理两个阶段31篇论文
大模型都火了这么久了,想必大家对LLM的上下文学习(In-Context Learning)能力都不陌生吧? 以防有的同学不太了解,今天我就来简单讲讲。 上下文学习(ICL)是一种依赖于大型语言模型的学习任务方式…...
WordPress(安装比子主题文件)zibll-7.5.1
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、新建网站二、配置ssl三.配置伪静态四.上传文件五.添加本地访问前言 提示:这里可以添加本文要记录的大概内容: 首先,我们要先理解什么是授权原理。 原理就是我们大家运营网站,点击授权…...
蓝桥杯 动态规划
01 数字三角形 #include<bits/stdc.h> using namespace std; const int N105; using lllong long; ll a[N][N],dp[N][N]; int main(){int n;cin>>n;for(int i1;i<n;i){for(int j1;j<i;j){cin>>a[i][j];}}for(int i5;i>1;i--){for(int j1;j<i;j){…...
【图论】重庆大学图论与应用课程期末复习资料2-各章考点(计算部分)(私人复习资料)
图论各章考点 二、树1、避圈法(克鲁斯克尔算法)2、破圈法3、Prim算法 四、路径算法1、Dijkstra算法2、Floyd算法 五、匹配1、匈牙利算法(最大权理想匹配(最小权权值取反)) 六、行遍性问题1、Fleury算法&…...
整数和浮点数在内存中的存储(大小端详解)
目录 一、整数在内存中的存储 二、大小端字节序和字节序判断 2.1为什么有大小端? 2.2请简述大端字节序和小端字节序的概念,设计一个小程序来判断当前机器的字节序。(10分)-百度笔试题 方法一(char*强制类型转换)…...
SpringBoot 集成 ChatGPT,实战附源码
1 前言 在本文中,我们将探索在 Spring Boot 应用程序中调用 OpenAI ChatGPT API 的过程。我们的目标是开发一个 Spring Boot 应用程序,能够利用 OpenAI ChatGPT API 生成对给定提示的响应。 您可能熟悉 ChatGPT 中的术语“提示”。在 ChatGPT 或类似语…...
数据结构——希尔排序(详解)
呀哈喽,我是结衣 不知不觉,我们的数据结构之路已经来到了,排序这个新的领域,虽然你会说我们还学过冒泡排序。但是冒泡排序的性能不高,今天我们要学习的希尔排序可就比冒泡快的多了。 希尔排序 希尔排序的前身是插入排…...
C++ day53 最长公共子序列 不相交的线 最大子序和
题目1:1143 最长公共子序列 题目链接:最长公共子序列 对题目的理解 返回两个字符串的最长公共子序列的长度,如果不存在公共子序列,返回0,注意返回的是最长公共子序列,与前一天的最后一道题不同的是子序…...
ubuntu中删除镜像和容器、ubuntu20.04配置静态ip
1 删除镜像 # 短id sudo docker rmi 镜像id # 完整id sudo docker rmi 镜像id# 镜像名【REPOSITORY:TAG】 sudo docker rmi redis:latest2 删除容器 # 删除某个具体容器 sudo docker rm 容器id# 删除Exited状态/未运行的容器,三种命令均可 sudo docker rm docker …...
华为手环 8 五款免费表盘已上线,请注意查收
华为手环 8,作为一款集时尚与实用于一体的智能手环,不仅具备强大的功能,还经常更新的表盘样式,让用户掌控时间与健康的同时,也能展现自己的时尚品味。这不,12 月官方免费表盘又上新了,推出了五款…...
JOSEF约瑟 同步检查继电器DT-13/200 100V柜内安装,板前接线
系列型号 DT-13/200同步检查继电器; DT-13/160同步检查继电器; DT-13/130同步检查继电器; DT-13/120同步检查继电器; DT-13/90同步检查继电器; DT-13/254同步检查继电器; 同步检查继电器DT-13/200 100V柜内板前接线 一、用途 DT-13型同步检查继电器用于两端供电线路的…...
景区旅游门户网站建设方案/谷歌推广代理公司
一、spring security介绍 Spring Security是一个功能强大且可高度自定义的身份验证和访问控制框架。它是保护基于Spring的应用程序的事实上的标准。 Spring Security是一个专注于为Java应用程序提供身份验证和授权的框架。与所有Spring项目一样,Spring Security的…...
贵港市城乡住房建设厅网站/整合营销的特点有哪些
分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!超越昨天的自己系列(7࿰…...
腾讯云 建立wordpress/aso优化师主要是干嘛的
下面我给大家带来一些数据库知识点和面试题。 下列的链接是我看到非常好的java基础面试题的博文推荐给大家。 http://blog.csdn.net/jackfrued/article/details/44921941/ 关系数据库这一块是非常重要的,对于我们初级程序员,数据库考察的很大一部分知…...
时时彩网站开发代理代码/最新注册域名查询
android:divider"drawable/shape"<!--分割线图片-->android:showDividers"middle|beginning|end" <!--分割线位置-->分割线如果是图片那就直接使用图片就行,如果要使用颜色就必须使用shape来显示,直接使用颜色或Color是…...
北京网站建设策划/百度信息流代运营
1 先到49服务器上,用nc发送消息 2 详细代码如下,注意:保存前先用 repartition(1),不然会有很多小文件 package cn.taobao; import org.apache.hadoop.io.NullWritable; import org.apache.hadoop.io.Text; import org.apache.had…...
浏阳做网站推荐/外链推广论坛
本站使用「署名 4.0 国际 (CC BY 4.0)」许可协议,欢迎转载、或重新修改使用,但需要注明来源。 署名 4.0 国际 (CC BY 4.0)本文作者: 苏洋创建时间: 2018年07月19日统计字数: 2595字阅读时间: 6分钟阅读原始链接: https://soulteary.com/2018/07/19/clean…...