来源:blog.csdn.net/randompeople/article/details/114917087
这个问题就是书本上没怎么讲解,就是告诉我们这样处理,但没有解释为什么这么处理?我也是基于这样的困惑去了解原因。
Java中提供了两种实现同步的基础语义:synchronized方法和synchronized块, 看个demo:
public class SyncTest {
\\ 1、synchronized方法
public synchronized void syncMethod(){
System.out.println("hello method");
}
\\ 2、synchronized块
public void syncBlock(){
synchronized (this){
System.out.println("hello block");
}
}
}
具体还要区分:
它具有的特性:
这样看来synchronized实现的锁是基于class对象来实现的,我们来看看如何实现的,它其实是跟class对象的对象头一起起作用的,对象在内存中的布局分为三块区域:对象头、实例数据和对齐填充。
其中对象头中有一个Mark Word,这里主要存储对象的hashCode、锁信息或分代年龄或GC标志等信息,把可能的情况列出来大概如下:

其中synchronized就与锁标志位一起作用实现锁。主要分析一下重量级锁也就是通常说synchronized的对象锁,锁标识位为10,其中指针指向的是monitor对象(也称为管程或监视器锁)的起始地址。
每个对象都存在着一个 monitor 与之关联,对象与其 monitor 之间的关系有存在多种实现方式,如monitor可以与对象一起创建销毁或当线程试图获取对象锁时自动生成,但当一个 monitor 被某个线程持有后,它便处于锁定状态。
在Java虚拟机(HotSpot)中,monitor是由ObjectMonitor实现的,其主要数据结构如下(位于HotSpot虚拟机源码ObjectMonitor.hpp文件,C++实现的):
ObjectMonitor() {
_header = NULL;
_count = 0; //记录个数
_waiters = 0,
_recursions = 0;
_object = NULL;
_owner = NULL;
_WaitSet = NULL; //处于wait状态的线程,会被加入到_WaitSet
_WaitSetLock = 0 ;
_Responsible = NULL ;
_succ = NULL ;
_cxq = NULL ;
FreeNext = NULL ;
_EntryList = NULL ; //处于等待锁block状态的线程,会被加入到该列表
_SpinFreq = 0 ;
_SpinClock = 0 ;
OwnerIsThread = 0 ;
}
上面有2个字段很重要:
_WaitSet队列处于wait状态的线程,会被加入到_WaitSet。_EntryList队列处于等待锁block状态的线程,会被加入到该列表。_owner_owner指向持有ObjectMonitor对象的线程我们来模拟一下进入锁的流程:
1、当多个线程同时访问一段同步代码时,首先会进入 _EntryList 集合
2、当线程获取到对象的monitor 后进入 _Owner 区域,并把monitor中的owner变量设置为当前线程同时monitor中的计数器count加1
3、若线程调用 wait() 方法,将释放当前持有的monitor,owner变量恢复为null,count自减1,同时该线程进入 WaitSet集合中等待被唤醒。
4、若当前线程执行完毕也将释放monitor(锁)并复位变量的值,以便其他线程进入获取monitor(锁)
这两个是Java对象都有的属性,表示这个对象的一个等待和通知机制。
推荐一个开源免费的 Spring Boot 最全教程:
参考其他博客,我们来看看不使用synchronized会怎么样,假设有2个线程,分别做2件事情,T1线程代码逻辑:
while(!条件满足) // line 1
{
obj.wait(); // line 2
}
doSomething();
T2线程的代码逻辑:
更改条件为满足; // line 1
obj.notify(); // line 2
多线程环境下没有synchronized,没有锁的情况下可能会出现如下执行顺序情况:
这样的执行顺序导致了notify通知发出去了,但没有用,已经wait是在之后执行,所以有人说没有保证原子性,就是line1 和line2 是一起执行结束,这个也被称作lost wake up问题。解决方法就是可以利用synchronized来加锁,于是有人就写了这样的代码:
synchronized(lock)
{
while(!条件满足)
{
obj.wait();
}
doSomething();
}
synchronized(lock)
{
更改条件为满足;
obj.notify();
}
这样靠锁来做达到目的。但这代码会造成死锁,因为先T1 wait(),再T2 notify();而问题在于T1持有lock后block住了,T2一直无法获得lock,从而永无可能notify()并将T1的block状态解除,就与T1形成了死锁。
所以JVM在实现wait()方法时,一定需要先隐式的释放lock,再block,并且被notify()后从wait()方法返回前,隐式的重新获得了lock后才能继续user code的执行。要做到这点,就需要提供lock引用给obj.wait()方法,否则obj.wait()不知道该隐形释放哪个lock,于是调整之后的结果如下:
synchronized(lock)
{
while(!条件满足)
{
obj.wait(lock);
// obj.wait(lock)伪实现
// [1] unlock(lock)
// [2] block住自己,等待notify()
// [3] 已被notify(),重新lock(lock)
// [4] obj.wait(lock)方法成功返回
}
doSomething();
}
其它线程API如PThread提供wait()函数的签名是类似cond_wait(obj, lock)的,因为同一个lock可以管多个obj条件队列。而Java内置的锁与条件队列的关系是1:1,所以就直接把obj当成lock来用了。因此此处就不需要额外提供lock,而直接使用obj即可,代码也更简洁:
synchronized(obj)
{
while(!条件满足)
{
obj.wait();
}
doSomething();
}
synchronized(lock)
{
更改条件为满足;
obj.notify();
}
wait/notify 如果不跟synchronized结合就会造成lost wake up,难以唤醒wait的线程,所以单独使用会有问题。
近期热文推荐:
1.1,000+ 道 Java面试题及答案整理(2022最新版)
4.别再写满屏的爆爆爆炸类了,试试装饰器模式,这才是优雅的方式!!
觉得不错,别忘了随手点赞+转发哦!
我正在学习如何使用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等等),但我确实想创建一个输出文件。
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
我在我的项目目录中完成了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