当前位置:网站首页>Synchronized和volatile 面试简单汇总
Synchronized和volatile 面试简单汇总
2022-07-31 14:50:00 【高个子男孩】
synchronized
Synchronized的作用主要有三个:
确保线程互斥的访问同步代码
保证共享变量的修改能够及时可见
有效解决重排序问题
从语法上讲,Synchronized总共有三种用法:
修饰普通方法
修饰静态方法
修饰代码块
synchronized修饰普通方法时,使用的锁是对象的锁。
synchronized修饰静态方法时,使用的锁是类对象的锁Class。
synchronized修饰的代码块 使用的锁是对象锁。
监视者monitorenter
当对象被占用时就会变成锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
如果monitor的进入数字为0,则该线程进入monitor,然后将进入数字设置为1,该线程即为monitor的所有者。
如果线程已经占有该monitor,只是重新进入,则进入显示器的进入数加1。
如果其他线程已经占用了monitor,则该线程进入双向状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。
监控退出monitorexit
执行monitorexit的线程必须是objectref所对应的monitor的所有者。
指令执行时,monitor的进入数减1,如果减1后进入数为0,那线程退出monitor,不再是这个monitor的所有者。其他被该monitor的线程可以尝试去获取这个monitor的所有权。
通过这两段描述,我们应该能很清楚的修剪Synchronized的实现原理,Synchronized的语义一致是通过一个monitor的对象来完成,实际上等待/通知等方法也依赖于monitor对象,这就是为什么只有在同步的块或方法中才能调用wait / notify等方法,否则会引发java.lang.IllegalMonitorStateException的异常的原因。
Synchronized底层实现方式
同步代码块

synchronized映射成字节码指令就是增加来两个指令:monitorenter和monitorexit。当一条线程进行执行的遇到monitorenter指令的时候,它会去尝试获得锁,如果获得锁那么锁计数+1(为什么会加一呢,因为它是一个可重入锁,所以需要用这个锁计数判断锁的情况),如果没有获得锁,那么阻塞。当它遇到monitorexit的时候,锁计数器-1,当计数器为0,那么就释放锁。
图上有2个monitorexit,synchronized锁释放有两种机制,一种就是执行完释放;另外一种就是发送异常,虚拟机释放。图中第二个monitorexit就是发生异常时执行的流程,就是“会有2个流程存在“。而且,从图中我们也可以看到在第13行,有一个goto指令,也就是说如果正常运行结束会跳转到19行执行。
同步方法
方法的同步并没有通过指令monitorenter和monitorexit来完成(理论上其实也可以通过这两条指令来实现),不过相对于普通方法,其常量池中多了ACC_SYNCHRONIZED标示符。
JVM就是根据该标示符来实现方法的同步的当方法调用时,调用指令将会检查方法的 ACC_SYNCHRONIZED 访问标志是否被设置,如果设置了,执行线程将先获取monitor,获取成功之后才能执行方法体,方法执行完后再释放monitor。
在方法执行期间,其他任何线程都无法再获得同一个monitor对象。其实本质上没有区别,只是方法的同步是一种隐式的方式来实现,无需通过字节码来完成。
synchronized与Lock的区别
类别 | synchronized | Lock |
存在层次 | Java的关键字,在jvm层面上 | 是一个类 |
锁的释放 | 1、以获取锁的线程执行完同步代码,释放锁 2、线程执行发生异常,jvm会让线程释放锁 | 在finally中必须释放锁,不然容易造成线程死锁 |
锁的获取 | 假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待 | 分情况而定,Lock有多个锁获取的方式,大致就是可以尝试获得锁,线程可以不用一直等待 |
锁状态 | 无法判断 | 可以判断 |
锁类型 | 可重入 不可中断 非公平 | 可重入 可判断 可公平(两者皆可) |
性能 | 少量同步 | 大量同步 |
wait()与sleep()的区别,简单来说wait()会释放对象锁而sleep()不会释放对象锁。
volatile
内存可见性
Java 内存模型(JMM):在 Java 中所有的共享变量都在主内存中,每个线程都有自己的工作内存,为了提高线程的运行速度,每个线程的工作内存都会把主内存中的共享变量拷贝一份进行缓存,以此来提高运行效率。但这样就会产生一个新的问题,如果某个线程修改了共享变量的值,其他线程不知道此值被修改了,就会发生两个线程值不一致的情况,
内存的可见性是指线程修改了变量的值之后,其他线程能立即知道此值发生了改变。
volatile 只是轻量级的线程可见方式,并不是轻量级的同步方式,所以并不能说 volatile 是轻量级的 synchronized
内存屏障的3个功能:
I. 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
II. 它会强制将对缓存的修改操作立即写入主存;
III. 如果是写操作,它会导致其他CPU中对应的缓存行无效。
边栏推荐
猜你喜欢
OAuth2:搭建授权服务器
Message queue data storage MySQL table design
MySQL [aggregate function]
使用 Chainlink Keepers 实现智能合约函数的自动化执行
梅克尔工作室-第一次
最小费用最大流问题详解
LeetCode二叉树系列——110.平衡二叉树
深入浅出边缘云 | 4. 生命周期管理
OpenShift 4 - Customize RHACS security policies to prevent production clusters from using high-risk registry
Why do we need to sub-library and sub-table?
随机推荐
Redis 】 【 publish and subscribe message
R语言向前或者向后移动时间序列数据(自定义滞后或者超前的期数):使用dplyr包中的lag函数将时间序列数据向前移动一天(设置参数n为正值)
UnityShader入门学习(一)——GPU与Shader
Use of el-tooltip
I summed up the bad MySQL interview questions
OpenShift 4 - 定制 RHACS 安全策略,阻断生产集群使用高风险 Registry
763.划分字母区间——之打开新世界
R语言ggplot2可视化:使用ggpubr包的ggboxplot函数可视化分组箱图、使用ggpar函数改变图形化参数(caption、添加、修改可视化图像的题注、脚注内容)
SetoolKit User Guide
Recommendation System - Recall Phase - 2013: DSSM (Twin Towers Model) [Embedding (Semantic Vector) Recall] [Microsoft]
MySQL 23道经典面试吊打面试官
435. 无重叠区间
梅克尔工作室-第一次
自适应控制——仿真实验三 用超稳定性理论设计模型参考自适应系统
英文语法-时与态
UnityShader入门学习(三)——Unity的Shader
R语言计算时间序列数据的移动平均值(滚动平均值、例如5日均线、10日均线等):使用zoo包中的rollmean函数计算k个周期移动平均值
NC | 中国农大草业学院杨高文组揭示发现多因子干扰会降低土壤微生物多样性的积极效应...
Open Inventor 10.12 Major Improvements - Harmony Edition
Getting started with UnityShader (3) - Unity's Shader