Synchronized的实现原理(一)

转载自 Synchronized的实现原理(一)

synchronized,是Java中用于解决并发情况下数据同步访问的一个很重要的关键字。当我们想要保证一个共享资源在同一时间只会被一个线程访问到时,我们可以在代码中使用synchronized关键字对类或者对象加锁。那么,本文来介绍一下synchronized关键字的实现原理是什么。在阅读本文之间,建议先看下Java虚拟机是如何执行线程同步的 

反编译

众所周知,在Java中,synchronized有两种使用形式,同步方法和同步代码块。代码如下:

/*** @author Hollis 17/11/9.*/
public class SynchronizedTest {public synchronized void doSth(){System.out.println("Hello World");}public void doSth1(){synchronized (SynchronizedTest.class){System.out.println("Hello World");}}
}

我们先来使用Javap来反编译以上代码,结果如下(部分无用信息过滤掉了):

  public synchronized void doSth();descriptor: ()Vflags: ACC_PUBLIC, ACC_SYNCHRONIZEDCode:stack=2, locals=1, args_size=10: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;3: ldc           #3                  // String Hello World5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V8: returnpublic void doSth1();descriptor: ()Vflags: ACC_PUBLICCode:stack=2, locals=3, args_size=10: ldc           #5                  // class com/hollis/SynchronizedTest2: dup3: astore_14: monitorenter5: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;8: ldc           #3                  // String Hello World10: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V13: aload_114: monitorexit15: goto          2318: astore_219: aload_120: monitorexit21: aload_222: athrow23: return

反编译后,我们可以看到Java编译器为我们生成的字节码。在对于doSthdoSth1的处理上稍有不同。也就是说。JVM对于同步方法和同步代码块的处理方式不同。

对于同步方法,JVM采用ACC_SYNCHRONIZED标记符来实现同步。 对于同步代码块。JVM采用monitorentermonitorexit两个指令来实现同步。

关于这部分内容,在JVM规范中也可以找到相关的描述。

同步方法

The Java® Virtual Machine Specification中有关于方法级同步的介绍:

Method-level synchronization is performed implicitly, as part of method invocation and return. A synchronized method is distinguished in the run-time constant pool's methodinfo structure by the ACCSYNCHRONIZED flag, which is checked by the method invocation instructions. When invoking a method for which ACC_SYNCHRONIZED is set, the executing thread enters a monitor, invokes the method itself, and exits the monitor whether the method invocation completes normally or abruptly. During the time the executing thread owns the monitor, no other thread may enter it. If an exception is thrown during invocation of the synchronized method and the synchronized method does not handle the exception, the monitor for the method is automatically exited before the exception is rethrown out of the synchronized method.

主要说的是: 方法级的同步是隐式的。同步方法的常量池中会有一个ACC_SYNCHRONIZED标志。当某个线程要访问某个方法的时候,会检查是否有ACC_SYNCHRONIZED,如果有设置,则需要先获得监视器锁,然后开始执行方法,方法执行之后再释放监视器锁。这时如果其他线程来请求执行方法,会因为无法获得监视器锁而被阻断住。值得注意的是,如果在方法执行过程中,发生了异常,并且方法内部并没有处理该异常,那么在异常被抛到方法外面之前监视器锁会被自动释放。

同步代码块

同步代码块使用monitorentermonitorexit两个指令实现。 The Java® Virtual Machine Specification 中有关于这两个指令的介绍:

monitorenter

Each object is associated with a monitor. A monitor is locked if and only if it has an owner. The thread that executes monitorenter attempts to gain ownership of the monitor associated with objectref, as follows:

If the entry count of the monitor associated with objectref is zero, the thread enters the monitor and sets its entry count to one. The thread is then the owner of the monitor.

If the thread already owns the monitor associated with objectref, it reenters the monitor, incrementing its entry count.

If another thread already owns the monitor associated with objectref, the thread blocks until the monitor's entry count is zero, then tries again to gain ownership.

monitorexit

The thread that executes monitorexit must be the owner of the monitor associated with the instance referenced by objectref.

The thread decrements the entry count of the monitor associated with objectref. If as a result the value of the entry count is zero, the thread exits the monitor and is no longer its owner. Other threads that are blocking to enter the monitor are allowed to attempt to do so.

大致内容如下: 可以把执行monitorenter指令理解为加锁,执行monitorexit理解为释放锁。 每个对象维护着一个记录着被锁次数的计数器。未被锁定的对象的该计数器为0,当一个线程获得锁(执行monitorenter)后,该计数器自增变为 1 ,当同一个线程再次获得该对象的锁的时候,计数器再次自增。当同一个线程释放锁(执行monitorexit指令)的时候,计数器再自减。当计数器为0的时候。锁将被释放,其他线程便可以获得锁。

总结

同步方法通过ACC_SYNCHRONIZED关键字隐式的对方法进行加锁。当线程要执行的方法被标注上ACC_SYNCHRONIZED时,需要先获得锁才能执行该方法。

同步代码块通过monitorentermonitorexit执行来进行加锁。当线程执行到monitorenter的时候要先获得所锁,才能执行后面的方法。当线程执行到monitorexit的时候则要释放锁。

每个对象自身维护这一个被加锁次数的计数器,当计数器数字为0时表示可以被任意线程获得锁。当计数器不为0时,只有获得锁的线程才能再次获得锁。即可重入锁。

至此,我们大致了解了Synchronized的原理。但是还有几个问题并没有介绍清楚,比如,Monitor到底是什么?对象的锁的状态保存在哪里? 别急,后面会再介绍。


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

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

相关文章

FOSS历史回顾:三代开源人的故事

现在是2016年,你环顾一下四周,开源早已无处不在了。开源无论是规范、形式、以及面貌都和最初的大相径庭,然而事实上,这也预示着新一代的开源程序员们的崛起。下面我们尝试解释下。 (以下这一段落为作者自谦&#xff09…

Spring中@Autowired、@Qualifier、@Resource的区别

转自: Spring中Autowired、Qualifier、Resource的区别_老周聊架构的博客-CSDN博客_qualifier和resource区别1、AutowiredAutowired 可以单独使用。如果单独使用,它将按类型装配。因此,如果在容器中声明了多个相同类型的bean,则会…

map分组后取前10个_海关数据 | 图解前10个月外贸

*内容转载自微信公众号:海关发布RECOMMEND【 推荐阅读 】海关数据 | 图解前三季度我国外贸海关数据 | 图解8月外贸海关数据 | 一图看懂前7个月外贸海关数据 | 图解上半年度外贸增3.9%声明本微信订阅号不以商业营利为目的,不排除部分文字内容或图片转载自…

回顾build 2016:你好,这是微软迄今最好的Windows开发平台

按:本文作者陈计节,ThoughtWorks 高级咨询师。多年的跨平台 .NET 开发者,全栈工程师,技术布道师。擅长互联网应用程序的设计、开发和运维等工作。 在最近的开发者大会(Build 2016)上,微软面向开…

深入理解多线程(二)—— Java的对象模型

转载自 深入理解多线程(二)—— Java的对象模型上一篇文章中简单介绍过synchronized关键字的方式,其中,同步代码块使用monitorenter和monitorexit两个指令实现,同步方法使用ACC_SYNCHRONIZED标记符实现。后面几篇文章会…

8.1-CPU结构(学习笔记)

【README】 本文总结自bilibili《计算机组成原理(哈工大刘宏伟)》的视频讲解,非常棒,墙裂推荐; 【1】CPU结构 Cpu的首要功能就是解释指令;功能列表如下: 1) 取指令:从内存中读取…

生物信息 python 书籍_用python做生物信息数据分析(1-环境准备)

写在前面四五年前,接触生物信息的时候,阴差阳错,我选择用perl。事实上,直到嫌我,我还是认为我当初的选择,完全正确!。在做一些小文本的快速处理上,perl在我看来,从来最优…

8.2-指令周期(学习笔记)

【README】 本文总结自bilibili《计算机组成原理(哈工大刘宏伟)》的视频讲解,非常棒,墙裂推荐; 【1】指令周期 【1.1】指令周期概述 1)指令周期:取出并执行一条指令所需的全部时间&#xff1…

深入理解多线程(三)—— Java的对象头

转载自 深入理解多线程(三)—— Java的对象头上一篇文章中我们从HotSpot的源码入手,介绍了Java的对象模型。这一篇文章在上一篇文章的基础上再来介绍一下Java的对象头。主要介绍一下对象头的作用,结构以及他和锁的关系。 Java对象…

python tkinter 背景色改变不了_python - Tkinter背景颜色问题 - 堆栈内存溢出

我有一个脚本,其中包含Tkinter模块,我想每隔3分钟更改一次背景颜色,例如绿色3分钟,然后橙色,然后红色。 我有显示绿色的代码,但无法更改它。当我在代码中创建函数时,会遇到一些不同的错误&#…

回顾微软近年来对于Linux和开源的策略

2014年十月,在旧金山举办的一场活动中,微软的CEO Satya Nadella向公众表示,微软“爱Linux”。作为昔日的竞争对手,微软对Linux的态度逐渐从敌对转变为合作。自那次发言以来,微软在开源方面频频重拳出击,似乎…

深入理解多线程(四)—— Moniter的实现原理

转载自 深入理解多线程(四)—— Moniter的实现原理本文是《深入理解多线程系列文章》的第四篇。点击查看原文,阅读该系列所有文章。 在深入理解多线程(一)——Synchronized的实现原理中介绍过关于Synchronize的实现原理…

(转)Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean

转自: Spring Boot通过ImportBeanDefinitionRegistrar动态注入Bean - 掘金在阅读SpringBoot源码时,看到SpringBoot中大量使用ImportBeanDefinitionRegistrar来实现Bean的动态注入。它是Spring中一个强大的扩展接口。本篇文章来https://juejin.cn/post/6…

通过图书编号查询python_Python图书接口调用代码实例

1.[代码][Python]代码#!/usr/bin/python# -*- coding: utf-8 -*-import json, urllibfrom urllib import urlencode#----------------------------------# 图书电商数据调用示例代码 - 聚合数据# 在线接口文档:http://www.juhe.cn/docs/50#-------------…

深入理解多线程(五)—— Java虚拟机的锁优化技术

转载自 深入理解多线程(五)—— Java虚拟机的锁优化技术本文是《深入理解多线程》的第五篇文章,前面几篇文章中我们从synchronized的实现原理开始,一直介绍到了Monitor的实现原理。这一篇在前几篇的基础上,深入介绍一下…

Visual Studio Code 1.0正式发布

Visual Studio Code 是一个运行于 OS X,Windows 和 Linux 之上的,针对于编写现代 web 和云应用的跨平台编辑器。 这标志着 Microsoft 第一次向开发者们提供了一款真正的跨平台编辑器。虽然完整版的 Visual Studio 仍然是只能运行在 Windows 之上&#xf…

springboot使用ImportBeanDefinitionRegistrar 动态注册bean

【README】 1.采用 ImportBeanDefinitionRegistrar 动态注册bean,应用场景有: 如 一个后端服务需要用到多个 rabbitmq集群客户端,kafka客户端;这时就需要手动注册多个同类型的bean,但不同beanName,并用 …

python 线性回归函数_Python实现的简单线性回归算法实例分析

本文实例讲述了Python实现的简单线性回归算法。分享给大家供大家参考,具体如下:用python实现R的线性模型(lm)中一元线性回归的简单方法,使用R的women示例数据,R的运行结果:> summary(fit)Call:lm(formula weight ~…

UWP应用模型概述

Andrew Clinick是微软的一名项目经理,在Build 2016大会上,他概括地讲述了通用Windows平台(UWP)应用模型的新特性。今年的其中一个亮点是,代号为Centennial的项目实现了桌面应用程序到UWP应用的转换。 Andrew一开始就阐…

8.3-指令流水(学习笔记)

【README】 本文总结自bilibili《计算机组成原理(哈工大刘宏伟)》的视频讲解,非常棒,墙裂推荐; 【1】如何提高机器速度 1 提高访存速度多体并行:对多个存储体进行交叉访问,在一个主存周期中&am…