欢迎大家来到IT世界,在知识的湖畔探索吧!
Java并发编程系列
- Java经典同步锁:Synchronized关键字
- Java面试宝典:final语义深度分析
- 经典hash算法:HashMap深度解析
前言
在Java中,Synchronized是解决并发访问共享数据的一种最简单实用的同步锁。
前面通过《Volatile深度剖析-可见性》、《Volatile深度剖析-指令重排序》、《Volatile深度剖析-原子性》三篇文章,我们知道了volatile是如何保证共享数据的可见性、有序性、原子性的。那Synchronized作为老牌同步锁,是怎么做的呢?
特性
1、可见性 保证工作线程修改共享变量及时同步到主内存
2、有序性 防止指令重排序(虚拟机指令、处理器指令)
3、原子性 多线程环境下,只能有一个线程获得锁保证数据原子性更改
原理
首先我们看段代码:
这是一段Synchronized很经典用法,在hello方法体使用Synchronized锁当前SynchronizedTests对象。
接下来看看编译后的代码:
我们发现hello和普通的方法差别在于多了monitorenter、monitorexit这个两个指令。而这个两个指令特殊在于执行hello代码时,多了两个监控器:进入监控器、退出监控器。
进入监控器:monitorenter指令
依据参考JVM规范:
每个对象持有一把监视器锁(monitor)。所有的线程通过执行monitorenter指令 尝试获取对象的监视器锁(monitor),如果A线程获取监视器锁,则监视器锁 处理锁定状态,其他线程访问被阻塞,直到A线程释放监视器锁,其他线程才能重新尝试获取监视器锁
欢迎大家来到IT世界,在知识的湖畔探索吧!
说的具体点就是对象监视器锁有个线程计数器,如果A线程获取到锁,则计数器为1,其他线程访问被阻塞。如果其他线程想获得锁,必须是A线程释放时,计数器值变为0。再则如果持有锁的A线程再次进入,则计数器加1。
退出监控器:monitorexit指令
当程序执行monitorexit指令时,计数器值会减1,直到值为0,那么监视器锁释放,线程退出,然后其他被阻塞线程可以尝试获取对象的监视器锁。
由此可知,同一时刻只会有一个线程获取到对象监视器锁,其他线程访问被阻塞。那么当持有锁的线程操作共享数据时(比如更新操作),是不会有其他线程线程干扰。
也就是说,同一时刻共享数据不会存在多个线程同时操作,从而保证共享数据的原子性、可见性、有序性。
与Volatile比较
1、Synchronized是通过monitorenter、monitorexit配对指令保证共享数据的可见性、有序性、原子性
2、Volatile是通过内存屏障以及处理器锁指令,把共享数据同步到主内存,保证共享数据的可见性、有序性
3、Volatile不能保证共享数据的原子性(比如++操作)
4、Volatile仅能修饰变量,Synchronized可以修饰变量、方法、Class
5、Volatile不会造成线程阻塞,Synchronized会造成线程阻塞
使用方式
第一、修饰代码块
被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
第二、 修饰普通方法
被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象。
第三 、修饰静态方法
其作用的范围是整个静态方法,作用的对象是这个类(Class)的所有对象。
第四、修饰类(Class)
其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
总结
Synchronized的用法简单安全,隐式锁使得程序员不需要编写额外的代码保证共享数据安全性,是Java程序员实现共享数据同步的最爱。
关于我
大家好,10年JAVA老兵,擅长微服务,分布式,并发,工作流。请大家多多关注我。
有需要Java资料的同学,可以关注之后私信哈,回复“资料”可以免费领取Java BAT面试宝典/微服务/SpringCloud/SpringBoot等视频和资料,亲记得要点赞转发哦!!!
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://itzsg.com/22822.html