操作系统锁的实现方法有哪几种_「从入门到放弃-Java」并发编程-锁-synchronized...

简介

上篇【从入门到放弃-Java】并发编程-线程安全中,我们了解到,可以通过加锁机制来保护共享对象,来实现线程安全。

synchronized是java提供的一种内置的锁机制。通过synchronized关键字同步代码块。线程在进入同步代码块之前会自动获得锁,并在退出同步代码块时自动释放锁。内置锁是一种互斥锁。

本文来深入学习下synchronized。

使用

同步方法

同步非静态方法

public class Synchronized { private static int count; private synchronized void add1() { count++; System.out.println(count); } public static void main(String[] args) throws InterruptedException { Synchronized sync = new Synchronized(); Thread thread1 = new Thread(() -> { for (int i = 0; i< 10000; i++) { sync.add1(); } }); Thread thread2 = new Thread(() -> { for (int i = 0; i< 10000; i++) { sync.add1(); } }); thread1.start(); thread2.start(); Thread.sleep(1000); System.out.println(count); }}

结果符合预期:synchronized作用于非静态方法,锁定的是实例对象,如上所示锁的是sync对象,因此线程能够正确的运行,count的结果总会是20000。

public class Synchronized { private static int count; private synchronized void add1() { count++; System.out.println(count); } public static void main(String[] args) throws InterruptedException { Synchronized sync = new Synchronized(); Synchronized sync1 = new Synchronized(); Thread thread1 = new Thread(() -> { for (int i = 0; i< 10000; i++) { sync.add1(); } }); Thread thread2 = new Thread(() -> { for (int i = 0; i< 10000; i++) { sync1.add1(); } }); thread1.start(); thread2.start(); Thread.sleep(1000); System.out.println(count); }}

结果不符合预期:如上所示,作用于非静态方法,锁的是实例化对象,因此当sync和sync1同时运行时,还是会出现线程安全问题,因为锁的是两个不同的实例化对象。

同步静态方法

public class Synchronized { private static int count; private static synchronized void add1() { count++; System.out.println(count); } private static synchronized void add11() { count++; System.out.println(count); } public static void main(String[] args) throws InterruptedException { Synchronized sync = new Synchronized(); Synchronized sync1 = new Synchronized(); Thread thread1 = new Thread(() -> { for (int i = 0; i< 10000; i++) { Synchronized.add1(); } }); Thread thread2 = new Thread(() -> { for (int i = 0; i< 10000; i++) { Synchronized.add11(); } }); thread1.start(); thread2.start(); Thread.sleep(1000); System.out.println(count); }}

结果符合预期:锁静态方法时,锁的是类对象。因此在不同的线程中调用add1和add11依然会得到正确的结果。

同步代码块

锁当前实例对象

public class Synchronized { private static int count; private void add1() { synchronized (this) { count++; System.out.println(count); } } private static synchronized void add11() { count++; System.out.println(count); } public static void main(String[] args) throws InterruptedException { Synchronized sync = new Synchronized(); Synchronized sync1 = new Synchronized(); Thread thread1 = new Thread(() -> { for (int i = 0; i< 10000; i++) { sync.add1(); } }); Thread thread2 = new Thread(() -> { for (int i = 0; i< 10000; i++) { sync1.add1(); } }); thread1.start(); thread2.start(); Thread.sleep(1000); System.out.println(count); }}

结果不符合预期:当synchronized同步方法块时,锁的是实例对象时,如上示例在不同的实例中调用此方法还是会出现线程安全问题。

锁其它实例对象

public class Synchronized { private static int count; public String lock = new String(); private void add1() { synchronized (lock) { count++; System.out.println(count); } } private static synchronized void add11() { count++; System.out.println(count); } public static void main(String[] args) throws InterruptedException { Synchronized sync = new Synchronized(); Synchronized sync1 = new Synchronized(); Thread thread1 = new Thread(() -> { for (int i = 0; i< 10000; i++) { sync.add1(); } }); Thread thread2 = new Thread(() -> { for (int i = 0; i< 10000; i++) { sync1.add1(); } }); thread1.start(); thread2.start(); Thread.sleep(1000); System.out.println(count); System.out.println(sync.lock == sync1.lock); }}
76f9d86f575475fa4ec3f237d42b2f22.png

结果不符合预期:当synchronized同步方法块时,锁的是其它实例对象时,如上示例在不同的实例中调用此方法还是会出现线程安全问题。

public class Synchronized { private static int count; public String lock = ""; private void add1() { synchronized (lock) { count++; System.out.println(count); } } private static synchronized void add11() { count++; System.out.println(count); } public static void main(String[] args) throws InterruptedException { Synchronized sync = new Synchronized(); Synchronized sync1 = new Synchronized(); Thread thread1 = new Thread(() -> { for (int i = 0; i< 10000; i++) { sync.add1(); } }); Thread thread2 = new Thread(() -> { for (int i = 0; i< 10000; i++) { sync1.add1(); } }); thread1.start(); thread2.start(); Thread.sleep(1000); System.out.println(count); System.out.println(sync.lock == sync1.lock); }}
001934f559f7de1a62068d4b09106c1c.png

结果符合预期:当synchronized同步方法块时,锁的虽然是其它实例对象时,但已上实例中,因为String = "" 是存放在常量池中的,实际上锁的还是相同的对象,因此是线程安全的

锁类对象

public class Synchronized { private static int count; private void add1() { synchronized (Synchronized.class) { count++; System.out.println(count); } } private static synchronized void add11() { count++; System.out.println(count); } public static void main(String[] args) throws InterruptedException { Synchronized sync = new Synchronized(); Synchronized sync1 = new Synchronized(); Thread thread1 = new Thread(() -> { for (int i = 0; i< 10000; i++) { sync.add1(); } }); Thread thread2 = new Thread(() -> { for (int i = 0; i< 10000; i++) { sync1.add1(); } }); thread1.start(); thread2.start(); Thread.sleep(1000); System.out.println(count); }}

结果符合预期:当synchronized同步方法块时,锁的是类对象时,如上示例在不同的实例中调用此方法是线程安全的。

锁机制

public class Synchronized { private static int count; public static void main(String[] args) throws InterruptedException { synchronized (Synchronized.class) { count++; } }}

使用javap -v Synchronized.class反编译class文件。

1637e9ae87165c111f767ea22f98e325.png

可以看到synchronized实际上是通过monitorenter和monitorexit来实现锁机制的。同一时刻,只能有一个线程进入监视区。从而保证线程的同步。

正常情况下在指令4进入监视区,指令14退出监视区然后指令15直接跳到指令23 return

但是在异常情况下异常都会跳转到指令18,依次执行到指令20monitorexit释放锁,防止出现异常时未释放的情况。

这其实也是synchronized的优点:无论代码执行情况如何,都不会忘记主动释放锁。

想了解Monitors更多的原理可以点击查看

锁升级

因为monitor依赖操作系统的Mutex lock实现,是一个比较重的操作,需要切换系统至内核态,开销非常大。因此在jdk1.6引入了偏向锁和轻量级锁。

synchronized有四种状态:无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁。

无锁

没有对资源进行锁定,所有线程都能访问和修改。但同时只有一个线程能修改成功

偏向锁

在锁竞争不强烈的情况下,通常一个线程会多次获取同一个锁,为了减少获取锁的代价 引入了偏向锁,会在java对象头中记录获取锁的线程的threadID。

  • 当线程发现对象头的threadID存在时。判断与当前线程是否是同一线程。
  • 如果是则不需要再次加、解锁。
  • 如果不是,则判断threadID是否存活。不存活:设置为无锁状态,其他线程竞争设置偏向锁。存活:查找threadID堆栈信息判断是否需要继续持有锁。需要持有则升级threadID线程的锁为轻量级锁。不需要持有则撤销锁,设置为无锁状态等待其它线程竞争。

因为偏向锁的撤销操作还是比较重的,导致进入安全点,因此在竞争比较激烈时,会影响性能,可以使用-XX:-UseBiasedLocking=false禁用偏向锁。

轻量级锁

当偏向锁升级为轻量级锁时,其它线程尝试通过CAS方式设置对象头来获取锁。

  • 会先在当前线程的栈帧中设置Lock Record,用于存储当前对象头中的mark word的拷贝。
  • 复制mark word的内容到lock record,并尝试使用cas将mark word的指针指向lock record
  • 如果替换成功,则获取偏向锁
  • 替换不成功,则会自旋重试一定次数。
  • 自旋一定次数或有新的线程来竞争锁时,轻量级锁膨胀为重量级锁。

CAS

CAS即compare and swap(比较并替换)。是一种乐观锁机制。通常有三个值

  • V:内存中的实际值
  • A:旧的预期值
  • B:要修改的新值
  • 即V与A相等时,则替换V为B。即内存中的实际值与我们的预期值相等时,则替换为新值。

CAS可能遇到ABA问题,即内存中的值为A,变为B后,又变为了A,此时A为新值,不应该替换。

可以采取:A-1,B-2,A-3的方式来避免这个问题

重量级锁

自旋是消耗CPU的,因此在自旋一段时间,或者一个线程在自旋时,又有新的线程来竞争锁,则轻量级锁会膨胀为重量级锁。

重量级锁,通过monitor实现,monitor底层实际是依赖操作系统的mutex lock(互斥锁)实现。

需要从用户态,切换为内核态,成本比较高

总结

本文我们一起学习了

  • synchronized的几种用法:同步方法、同步代码块。实际上是同步类或同步实例对象。
  • 锁升级:无锁、偏向锁、轻量级锁、重量级锁以及其膨胀过程。

synchronized作为内置锁,虽然帮我们解决了线程安全问题,但是带来了性能的损失,因此一定不能滥用。使用时请注意同步块的作用范围。通常,作用范围越小,对性能的影响也就越小(注意权衡获取、释放锁的成本,不能为了缩小作用范围,而频繁的获取、释放)。

本文作者:aloof_

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

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

相关文章

android长按加入购物车,《Android APP可能有的东西》之UI篇:加入购物车动画

很多电商app的加入购物车的动作会要求加上动画效果&#xff1a;飞进购物车&#xff0c;想来也合理&#xff0c;在listview界面时商品快速加入购物车&#xff0c;一直toast用户加入成功好像不太正常&#xff0c;所以添加一个动画&#xff0c;用户自然就懂了&#xff0c;而且也挺…

基于android 定位系统,基于Android平台定位系统设计和实现

2 0 1 3牟第 1 2期文章编号&#xff1a; 1 0 0 9— 2 5 5 2 ( 2 0 1 3 ) 1 2— 0 1 8 7— 0 4 中图分类号&#xff1a; T P 3 1 6 . 8 9 文献标识码&#xff1a; A基于 A n d r o i d平台定位系统设计和实现李瑞宣&#xff0c;王山东&#xff0c;徐志远&#xff0c;王伶俐(河海…

开发工评价程师自我_常见“自我评价”写作范例

“自我评价”在找工作的时候是一个非常重要的内容&#xff0c;优秀的“自我评价”不仅就可以让别人对你有一个全面的认识&#xff0c;更可以通过寥寥数语就让别人对你产生浓厚的兴趣&#xff0c;让你的求职之路事半功倍。但是怎么写“自我评价”&#xff0c;却是让很多人犯愁的…

visual studio 调试python_Visual Studio Code Python 调试设置

很意外Visual Studio Code居然支持Python代码的断点调试。一起来配置一下。工具/原料 Visual Studio Code 1.1 Python 2.7.11 方法/步骤 1 首先&#xff0c;当然是要先安装插件&#xff0c;配置Python环境。这个大家看这个文章 2 环境配置完成后&#xff0c;我们点击调试按钮&a…

leetcode c程序总提示主函数_Matlab系列之函数嵌套

昨天的那一篇讲的几个函数&#xff0c;不知道你们理解的如何&#xff0c;是否懂得怎么去使用了&#xff0c;如果还没懂&#xff0c;一定要再多看几遍&#xff0c;并且去在软件上进行实操&#xff0c;今天的话&#xff0c;将要介绍一下函数的嵌套&#xff0c;不过在正式讲嵌套之…

华为手机出现android啥意思,传华为正研发手机系统,如果脱离安卓系统,还有啥能阻止华为前进...

原标题&#xff1a;传华为正研发手机系统&#xff0c;如果脱离安卓系统&#xff0c;还有啥能阻止华为前进自从国产手机正式进入了智能手机时代之后&#xff0c;中华酷联的中兴、酷派、联想早已经被华为甩在了身后。虽然现在国产手机已经呈现出华为、小米、OPPO、vivo四足鼎立的…

sql 数据库前两列值乘_Sql语句常用关键字

最近接触sql比较多&#xff0c;发现自己已经遗忘的也差不多&#xff0c;要用到的时候迟迟拿不出来&#xff0c;今天开始会在知乎上纪录一些sql语句学习的内容&#xff0c;内容重在说明查询语句的用法。一、sql查询语句的初始介绍1、查询语句的一般写法&#xff1a;select .....…

谷歌fuchsiaos和华为鸿蒙,华为鸿蒙最大的对手现身!谷歌正式推送Fuchsia OS,或替代安卓...

原标题&#xff1a;华为鸿蒙最大的对手现身&#xff01;谷歌正式推送Fuchsia OS&#xff0c;或替代安卓可能是看到了华为鸿蒙的进展神速&#xff0c;谷歌在近日也正是开始了Fuchsia OS的推送。5月25日&#xff0c;谷歌Fuchsia OS项目负责人在社交媒体上公开喊话&#xff1a;“今…

c html转为datatable,C#中DataTable导出为HTML格式的方法

前言在C#中DataTable导出数据的时候&#xff0c;我们需要HTML格式的输出数据, 这时候就需要使用将DataTable导出为到HTML格式的方法了&#xff0c;以下代码就可以帮助我们达到目的。首先,我们要绑定DataTable和 DataGridView。一、通过DataTable绑定DataGridView1. 创建DataTab…

mybatis insert 忽略 联合唯一索引_MySQL实战中,Insert语句的使用心得总结

提到MySQL的Insert语句&#xff0c;你肯定不陌生&#xff0c;或许已经张口就来&#xff1a;不就是insert into table values(xxx,xxx,xxx)嘛&#xff01;没错&#xff0c;但在实战中&#xff0c;根据不同的需求场景&#xff0c;插入操作在语法、执行方式上的用法多种多样。今天…

eureka集群只注册一个_Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇

Spring cloud系列教程第十篇- Spring cloud整合Eureka总结篇本文主要内容&#xff1a;1&#xff1a;spring cloud整合Eureka总结本文是由凯哥(凯哥Java:kagejava)发布的《spring cloud系列》教程的总第十篇&#xff1a;本文是几个维度中的第一个维度&#xff1a;注册与发现维度…

html鼠标滚轴后下一页,鼠标滚动有一页ppt不能马上下翻,而是上下移动,移动到一定位置后才翻到下一页,这是怎么回事?怎么解决?...

一般是这张幻灯片里有图片才会出现这种问题&#xff0c;你的图片格式有问题&#xff0c;点击图片&#xff0c;再点击格式&#xff0c;把图片格式换一下就好了同问&#xff0c;但不是下面两个原因&#xff0c;都试过没用1&#xff0c;ppt的比例太大&#xff0c;缩小比例即可。Ct…

resnet keras 结构_Day146:第二讲 ResNet

出处论文&#xff1a;Deep Residual Learning for Image Recognition作者&#xff1a;Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian SunImageNet Top5错误率&#xff1a; 3.57%主要思想主要体现在 Residual(残差)&#xff0c;从名字就可以看出&#xff0c;不学绝对值&#x…

html5内容切换特效,html5+jQuery图片和文字内容同时左右切换特效

html5jQuery图片和文字内容同时左右切换特效&#xff0c;点击图片或者点击左右按钮进行切换&#xff0c;图片转动以及文字内容动画效果切换。查看演示下载资源&#xff1a;22次 下载资源下载积分&#xff1a;20积分js代码 (function(){var bannerIndex 0;var $bannerBgs $(.j…

asp向不同的用户发送信息_【asp.net core 系列】 1 带你了解一下asp.net core

0. 前言 这是一个新的系列&#xff0c;名字是《http://ASP.NET Core 入门到实战》。这个系列主讲http://ASP.NET Core MVC&#xff0c;辅助一些前端的基础知识&#xff08;能用来实现我们需要的即可&#xff0c;并非主讲&#xff09;。同时这个系列也会在后续介绍http://ASP.NE…

html 怎么使用http请求数据类型,HTTP请求方式中8种请求方法(简单介绍)

HTTP工作原理HTTP协议定义Web客户端如何从Web服务器请求Web页面&#xff0c;以及服务器如何把Web页面传送给客户端。HTTP协议采用了请求/响应模型。客户端向服务器发送一个请求报文&#xff0c;请求报文包含请求的方法、URL、协议版本、请求头部和请求数据。服务器以一个状态行…

上行30m下行200m是多少宽带_套餐内有多少流量,就加送多少流量!电信流量攻势太凶猛!...

虽然6月6日国内发放了5G商用牌照&#xff0c;5G时代正式开启。但是对于用户来说&#xff0c;要想用上5G服务尚需等待一段时间&#xff0c;因为现在5G手机终端尚未普及、5G资费套餐也未出炉、运营商的5G网络还需要通过建设进一步扩大覆盖范围。在5G真正走近用户的前夕&#xff0…

vue项目html引入css,vue项目引入自定义.css的样式文件

ES6的引入方式&#xff1a;.vue文件中css文件引入import "../assets/common/common.css";//自定义.css的样式路径js文件的引入在main.js中&#xff1a;import API from ./assets/api/api.config.jsVue.prototype.$API API;P.S.&#xff1a;传统上&#xff0c;引入cs…

485通信原理_上位机开发之单片机通信实践

经常会有一些学员会问到上位机与单片机之间通信的问题&#xff0c;而我们经常会讲上位机与PLC之间通信&#xff0c;那么其实对上位机开发来说&#xff0c;不管是和PLC通信&#xff0c;还是和单片机通信&#xff0c;通信原理都是一样的。PLC的本质就是单片机&#xff0c;在单片机…

计算机数媒专业优势,27所院校新开设数字媒体艺术专业,“数媒”专业为什么这么火?...

原标题&#xff1a;27所院校新开设数字媒体艺术专业&#xff0c;“数媒”专业为什么这么火&#xff1f;今年2月&#xff0c;教育部公布了2020年度普通高等学校本科专业备案和审批结果。其中&#xff0c;有包括华中农业大学在内的全国27所院校新增备案了“数字媒体艺术”本科专业…