操作系统锁的实现方法有哪几种_「从入门到放弃-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,一经查实,立即删除!

相关文章

cocos2dx标准容器_cocos2dx[3.2](24)——内存管理机制

【唠叨】整合参考文档。【参考】【内存管理机制】在3.x版本&#xff0c;Cocos2d-x采用全新的根类 Ref&#xff0c;实现Cocos2d-x 类对象的引用计数记录。引擎中的所有类都派生自Ref。1、引用计数Cocos2d-x 提供引用计数管理内存。> 调用 retain() 方法 &#xf…

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

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

python里的shell是什么_python中的shell操作

http://blog.csdn.net/meng_tianshi/article/details/6682317 首先介绍一个函数&#xff1a; os.system(command) 这个函数可以调用shell运行命令行command并且返回它的返回值。试一下在python的解释器里输入os.system(”ls-l”)&#xff0c;就可以看到”ls”列出了当前目录下的…

基于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;王伶俐(河海…

集合竞价如何买入_集合竞价买入法 如何在集合竞价中买入股票?

集合竞价买入法、 如何在集合竞价中买入股票&#xff1f;要想集合竞价买入涨停板&#xff0c;要做到如下3点&#xff1a;快、准、狠&#xff01;文章将为你盘点这几个方法的具体做法&#xff0c;希望对股民有所帮助。快&#xff1a;就是利用9&#xff1a;15-9&#xff1a;25这1…

python 公众号爬虫_python_爬虫_微信公众号抓取

importrequests,pymysqlimportjson,jsonpath,random,re,time,datetime,os,imghdrfrom lxml importetreefrom selenium importwebdriverfrom urllib importrequestimportssl ssl._create_default_https_contextssl._create_unverified_context注意点&#xff1a;如果同一时间内刷…

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

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

android调用服务器端口,更改运行adb服务器的默认端口(即5037)

使用环境variablesANDROID_ADB_SERVER_PORT选择端口。以下在bash下工作&#xff1a;$ export ANDROID_ADB_SERVER_PORT12345 $ adb start-server * daemon not running. starting it now on port 12345 * * daemon started successfully * $ adb devices List of devices attac…

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…

cad图纸比对lisp_CompareDWG|CAD图纸比较软件(CompareDWG)下载v2018 官方版 - 欧普软件下载...

CompareDWG是一款免费的CAD图纸比较软件&#xff0c;可以帮助用户快速找到两个dwg图像之间的差异&#xff0c;使用不同的颜色进行标注&#xff0c;生成详细的报表&#xff0c;相比人工查找效率更高&#xff0c;准确率也高&#xff0c;支持几乎所有的autocad版本&#xff0c;适用…

android 电话 快捷键,Android studio开发常用快捷键详解

最常用快捷键1.Ctrl&#xff0b;E可以显示最近编辑的文件列表2.Shift&#xff0b;Click(点击)可以关闭文件3.Ctrl&#xff0b;[或者ctrl]可以跳到大括号的开头结尾4.Ctrl&#xff0b;Shift&#xff0b;Backspace可以跳转到上次编辑的地方5.Ctrl&#xff0b;F12可以显示当前文件…

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

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

alibab仓库 idea_01.微服务架构编码、构建

教学视频传送&#xff1a;springBoot和springCloud的版本选型https://start.spring.io/actuator/info查看json串返回结果这就是我们的选型依据本次开发选用版本如下&#xff1a;cloud : Hoxton.SR1boot : 2.2.2.RELEASEcloud alibaba : 2.1.0.RELEASEjava …

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

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

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

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

kayui进行添加_关于layui 实现点击按钮添加一行(方法渲染创建的table)

目标&#xff1a;layui 实现点击按钮添加一行解决方案&#xff1a;方案1、table 是用转换静态表格的方式创建的&#xff0c;写一个button&#xff0c;每次点击按钮&#xff0c;就添加一个 标签&#xff1b;方案2、table 是用方法渲染的方式创建的&#xff0c;写一个button,每次…

android怎样添加图片锐化功能,如何在android处理图片(图像二值化锐化转换格式).doc...

如何在android处理图片(图像二值化锐化转换格式)链接&#xff1a;如何在android处理图片( 图像二值化、锐化、转换格式)/thread-36559-1-1.htmlpackage net.wealthgod.client.center.ocr;import java.awt.Graphics2D;import java.awt.color.ColorSpace;import java.awt.geom.Af…

python的open方法_Python os.open() 方法

Python os.open() 方法 概述 os.open() 方法用于打开一个文件&#xff0c;并且设置需要的打开选项&#xff0c;模式参数mode参数是可选的&#xff0c;默认为 0777。 语法 open()方法语法格式如下&#xff1a; os.open(file, flags[, mode]); 参数 file -- 要打开的文件 flags -…

postforobject 设置代理_OAuth2RestTemplate中的代理配置

我需要使用由OAuth2保护的API.为此我使用OAuth2RestTemplate.但我得到以下错误&#xff1a;java.net.ConnectException: Connection timed out: connect由于代理问题,这种情况正在发生.我知道如何在RestTemplate中设置代理&#xff1a;SimpleClientHttpRequestFactory clientHt…

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

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