Java提供了多种机制实现多线程之间有需要同步执行的场景需求。其中最基本的是Synchronized ,实现上使用对象监视器( Monitor )。
Java中的每个对象都是与线程可以锁定或解锁的对象监视器( Monitor )关联。在同一时间只有一个线程可以在对象监视器( Monitor )上保持锁定。任何其他线程试图锁定对象监视器( Monitor )都会被阻止,直到它们可以获得该监视器上的锁定。
Synchronized 的作用范围,依据锁定的对象(object、this、class)、使用方式,可以分成五种情况。如果按照JVM字节码的区别,也可以分成两种形式:代码块(monitorenter、monitorexit)、函数(ACC_SYNCHRONIZED)。
虽然可以按照不同维度来划分 Synchronized 但本身机制是一样的,无论是 Synchronized 函数/代码块,都是通过对象监视器( Monitor )来实现。无论是this、class、object本质上都是一个对象,区别无非代表的是当前实例、类、一般实例,它们都有着对象监视器( Monitor )。
在HotSpot虚拟机中,对象监视器( Monitor ) 具体的实现类就是 ObjectMonitor(C++)。
在使用/分析 Synchronized 同步是否有效正确的时候,只需要分析需要的同步块是否作用在同一个对象监视器( Monitor )上。换一种描述,是否作用在同一个对象(Object)上,这里(Object)可以是this、object、class。

下面分别按照Synchronized 代码块、Synchronized 函数维度来进行详细介绍。
Synchronized 代码块的一般使用形式:synchronized ( Expression ) Block 。
Expression 必须是一个对象,可以是class、this、object,不能是原始类型(int、float...);否则编译的时候就会报错。如果 Expression 是null,会抛出NullPointerException 的异常。
Block表示一段逻辑代码,执行逻辑代码前会锁定Expression 的对象监视器( Monitor )。如果正常运行完成后,对象监视器( Monitor )会被释放;如果运行期间异常/中断了同样的也会释放对象监视器( Monitor )。先加锁确保其他线程无法进入执行,所以Synchronized 是悲观锁,JVM指令上使用monitorenter、monitorexit 来进行相关实现。

在字节码指令里可以也可以看到有两个monitorexit ,一个是正常运行后的释放;另一个是在异常(athrow)抛出前的释放。同一个线程可以多次进入被锁定的相同对象监视器( Monitor ),所以Synchronized 是可重入锁。

Synchronized函数在同步原理上同 Synchronized代码块是没有区别的,都是通过锁定对象监视器( Monitor );区别在于这里的对象是隐藏了起来。同样的支持可重入。
如果是静态方法(static),锁定的对象是这个方法所在的class object 对象。
如果是普通的方法,锁定的是this(当前实例)对象。
编译成JVM字节码的时候,函数描述上会标识ACC_SYNCHRONIZED ,并不会在函数代码块中显示的使用monitorenter、monitorexit指令。
在调用函数前锁定对象监视器( Monitor ),完成运行后释放对象监视器( Monitor )。无论函数是否有显性的抛出/处理异常,如果有异常中断抛出前也会自动的释放锁定的对象监视器( Monitor )。

Synchronized同步一直也在进行优化,也是跟随着JDK新理念一起发展。比如偏向锁、轻量锁、重量锁、适应性自旋等等机制。不同的JDK版本,不同的JVM可能都有所不同。
在HotSpot虚拟机中,抛开锁升级、自适应等机制;基本原理是线程通过 CAS抢占对象监视器( Monitor ) _Owner来实现锁,没有抢占的会进入 _EntryList 来进行放置。当然, 线程执行/中断释放_Owner后,_EntryList并不是简单按照FIFO来进行选择执行不会保证公平性,所以Synchronized是非公平锁。

图中_WaitSet没有体现用途,但其是很重要的一个结构, 用于当 _Owner 执行线程中断时,线程将会写入。值得注意获取到锁之后才能中断,等待锁时不可中断。当相关线程被唤醒后,会采有不同的策略重新回到_EntryList 或者 参与CAS竞争 _Owner,这里存在线程上下文切换的可能。
详细可以查看相关源码:
http://hg.openjdk.java.net/jdk8u/jdk8u/hotspot/file/69087d08d473/src
在最后综述下Synchronized 特性:悲观锁、可重入锁、非公平锁。
欢迎长期关注公众号/头条号(Java研究者)
我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div
我有一个Ruby程序,它使用rubyzip压缩XML文件的目录树。gem。我的问题是文件开始变得很重,我想提高压缩级别,因为压缩时间不是问题。我在rubyzipdocumentation中找不到一种为创建的ZIP文件指定压缩级别的方法。有人知道如何更改此设置吗?是否有另一个允许指定压缩级别的Ruby库? 最佳答案 这是我通过查看rubyzip内部创建的代码。level=Zlib::BEST_COMPRESSIONZip::ZipOutputStream.open(zip_file)do|zip|Dir.glob("**/*")d
类classAprivatedeffooputs:fooendpublicdefbarputs:barendprivatedefzimputs:zimendprotecteddefdibputs:dibendendA的实例a=A.new测试a.foorescueputs:faila.barrescueputs:faila.zimrescueputs:faila.dibrescueputs:faila.gazrescueputs:fail测试输出failbarfailfailfail.发送测试[:foo,:bar,:zim,:dib,:gaz].each{|m|a.send(m)resc
很好奇,就使用rubyonrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提
假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在尝试使用ruby和Savon来使用网络服务。测试服务为http://www.webservicex.net/WS/WSDetails.aspx?WSID=9&CATID=2require'rubygems'require'savon'client=Savon::Client.new"http://www.webservicex.net/stockquote.asmx?WSDL"client.get_quotedo|soap|soap.body={:symbol=>"AAPL"}end返回SOAP异常。检查soap信封,在我看来soap请求没有正确的命名空间。任何人都可以建议我
关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。
我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t
我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h
我想为Heroku构建一个Rails3应用程序。他们使用Postgres作为他们的数据库,所以我通过MacPorts安装了postgres9.0。现在我需要一个postgresgem并且共识是出于性能原因你想要pggem。但是我对我得到的错误感到非常困惑当我尝试在rvm下通过geminstall安装pg时。我已经非常明确地指定了所有postgres目录的位置可以找到但仍然无法完成安装:$envARCHFLAGS='-archx86_64'geminstallpg--\--with-pg-config=/opt/local/var/db/postgresql90/defaultdb/po