是时候来唠一唠synchronized关键字了,Java多线程的必问考点!
写在开头
在之前的博文中,我们介绍了volatile关键字,Java中的锁以及锁的分类,今天我们花5分钟时间,一起学习一下另一个关键字:synchronized。
synchronized是什么?
首先synchronized是Java中的一个关键字,所谓关键字,就是Java中根据底层封装所赋予的一种具有特殊语义的单词,而synchronized译为同步之意,可保证在同一时刻,被它修饰的方法或代码块只能有一个线程执行,它的使用解决了并发多线程中的三大问题:原子性、可见性、顺序性。
很多小伙伴在过往的书籍中可能会看到说synchronized是一种重量级锁,性能差,不建议在代码中使用,其实这是早期的synchronized特点,自JDK1.6之后,synchronized 引入了大量的优化如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销,这些优化让 synchronized 锁的效率提升了很多。因此, synchronized 还是可以在实际项目中使用的,像 JDK 源码、很多开源框架都大量使用了 synchronized 。
synchronized的使用
synchronized在Java中主要的3种使用方式:
- 修饰实例方法: 为当前对象实例加锁,进入同步方法需要先获取对象锁;
- 修饰静态方法: 为当前类加锁,锁定的是Class对象,进入同步方法需要先获取类锁;
- 修饰代码块: 为指定对象加锁,进入同步方法需要先获取指定对象的锁。
样例:
//修饰实例方法,为当前实例加锁
synchronized void method() {
//业务代码
}
//修饰静态方法,锁为当前Class对象
synchronized static void method() {
//业务代码
}
//修饰代码块,锁为括号里面的对象
synchronized(this) {
//业务代码
}
写到这里,突然想到了3个面试可能会考的知识点,列举一下!
问题1:synchronized修饰代码块可以给类加锁吗?
当然可以!我们前面说了修饰代码块时,是给代码中的对象加锁,这里面的对象既可以是实例也可以是类。
问题2:静态 synchronized 方法和非静态 synchronized 方法之间的调用互斥么?
不互斥!如果线程A调用一个实例对象的非静态synchronized方法,线程B同时去调用这个实例对象所属类的静态synchronized方法并不会发生互斥,因为线程A此时拿到的是实例对象锁,而线程B拿到的是当前类的锁。
问题3:构造方法可以用 synchronized 修饰么?
不可以!构造方法本身就是线程安全的,在Java开发规范里也明确告诉我们
构造方法不能是抽象的(abstract)、静态的(static)、最终的(final)、同步的(synchronized)。
synchronized的底层原理
在synchronized的底层(JVM层面),针对方法与代码块的实现逻辑是不同的,因此我们在分析底层原理是也要分别来看。
1、当synchronized修饰方法时
public class Test {
public synchronized void method() {
System.out.println("synchronized 方法");
}
}
我们通过对编译后的class文件进行反编译后,分析其底层实现。
知识点扩展:
我们通过javap命令进行反编译,javap是Java class文件分解器,可以反编译,也可以查看java编译器生成的字节码等。
javap参数如下:

使用方式,既可以在电脑的命令行提示符中使用,也可以通过idea的terminal终端使用,我这里采用idea中进行反汇编操作。参考命令:javap -c -v Test.class
【反汇编结果】

由上图可看出同步方法通过加 ACC_SYNCHRONIZED 标识实现线程的执行权的控制,如果修饰的是实例方法,JVM会获取对象锁,如果修饰的是静态方法,JVM会获取当前类锁。
2、当synchronized修饰代码块时
public class Test {
public void method() {
synchronized (this) {
System.out.println("synchronized");
}
}
}
【反汇编结果】

与同步方法不同,同步代码块中使用了monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置,并且monitorexit标识有2个,以保证在正常执行和异常情况下均可释放锁。
在命令执行到monitorenter时,线程会去尝试获取对象得锁,这里也可称之为对象所对应的monitor所有权。写到这里,我们又要做一个知识点扩展啦。
知识点扩展:
在JVM中monitor的底层基于C++实现,被称之为对象监视器,每个对象都会内置一个ObjectMonitor与之关联,关联的起始地址存于对象头的MarkWord中。

ObjectMonitor几个关键属性:
- _owner:指向持有ObjectMonitor对象的线程
- _WaitSet:存放处于wait状态的线程队列
- EntryList:存放处于等待锁block状态的线程队列
- recursions:锁的重入次数
- _count:用来记录该线程获取锁的次数
当多个线程同时访问同步代码时,会被放入EntryList中,根据线程优先级尝试获取对象锁,如果锁的计数器为 0 则表示锁可以被获取,获取到锁的线程进入owner区域,count加1,这里其实还有之前说的object中的wait/notify/notifyall的组合,也依赖monitor,所以他们才必须用在同步方法或代码块中。
对象锁的的拥有者线程才可以执行 monitorexit 指令来释放锁。在执行 monitorexit 指令后,将锁计数器设为 0,表明锁被释放,其他线程可以尝试获取锁。
总结
关于synchronized的介绍其实远没有结束,还有很多细节可以值得学习,我们会在后面的文章中逐渐补充,避免文章过长,读者失去阅读的耐心!
结尾彩蛋
如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

如果您想与Build哥的关系更近一步,还可以关注“JavaBuild888”,在这里除了看到《Java成长计划》系列博文,还有提升工作效率的小笔记、读书心得、大厂面经、人生感悟等等,欢迎您的加入!

是时候来唠一唠synchronized关键字了,Java多线程的必问考点!的更多相关文章
- Java多线程同步 synchronized 关键字的使用
代表这个方法加锁,相当于不管哪一个线程A每次运行到这个方法时,都要检查有没有其它正在用这个方法的线程B(或者C D等),有的话要等正在使用这个方法的线程B(或者C D)运行完这个方法后再运行此线程A, ...
- 【转】Java多线程编程中易混淆的3个关键字( volatile、ThreadLocal、synchronized)总结
概述 最近在看<ThinKing In Java>,看到多线程章节时觉得有一些概念比较容易混淆有必要总结一下,虽然都不是新的东西,不过还是蛮重要,很基本的,在开发或阅读源码中经常会遇到,在 ...
- Java多线程(三)—— synchronized关键字详解
一.多线程的同步 1.为什么要引入同步机制 在多线程环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源.必须对这种潜在资源冲突进行预防. 解决方法:在线程使用一个资源时为其加锁即可. 访问资 ...
- Java多线程学习(二)synchronized关键字(2)
转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79670775 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...
- Java多线程学习(二)synchronized关键字(1)
转载请备注地址: https://blog.csdn.net/qq_34337272/article/details/79655194 Java多线程学习(二)将分为两篇文章介绍synchronize ...
- Java:synchronized关键字引出的多种锁
前言 Java 中的 synchronized关键字可以在多线程环境下用来作为线程安全的同步锁.本文不讨论 synchronized 的具体使用,而是研究下synchronized底层的锁机制,以及这 ...
- 用代码说话:synchronized关键字和多线程访问同步方法的7种情况
synchronized关键字在多线程并发编程中一直是元老级角色的存在,是学习并发编程中必须面对的坎,也是走向Java高级开发的必经之路. 一.synchronized性质 synchronized是 ...
- synchronized关键字的使用
synchronized关键字是java并发编程中常使用的同步锁,用于锁住方法或者代码块,锁代码块时可以是synchronized(this){}.synchronized(Object){}.syn ...
- java线程基础巩固---数据同步引入并结合jconsole,jstack以及汇编指令认识synchronized关键字
对于多线程编程而言其实老生成谈的就是数据同步问题,接下来就会开始接触这块的东东,比较麻烦,但是也是非常重要,所以按部就班的一点点去专研它,下面开始. 数据同步引入: 这里用之前写过的银行叫号的功能做为 ...
- java多线程——同步块synchronized详解
Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静 ...
随机推荐
- Power BI 1 DAY
目录 Power BI(商业智能)分析 BI 分析步骤 Power Query 表数据结构 区别 主键 Power Query中的纵向合并与横向合并 销售一表和销售二表进行纵向合并为一张销售表. 产品 ...
- NC22594 Rinne Loves Graph
题目链接 题目 题目描述 Island 发生了一场暴乱!现在 Rinne 要和 Setsuna 立马到地上世界去. 众所周知:Island 是有一些奇怪的城镇和道路构成的(题目需要,游戏党勿喷),有些 ...
- python 中异常类型总结
异常类型: 异常名称 描述BaseException 所有异常的基类SystemExit 解释器请求退出KeyboardInterrupt ...
- Git将某个文件合并到指定分支
企业开发中,经常会单独拉分支去做自己的需求开发,但是某些时候一些公共的配置我们需要从主线pull,这时候整个分支merge显然不合适 1.切换至待合并文件的分支 git checkout <br ...
- python 创建动态类
一般情况下多数是预先定义类 而少数特殊情况就需要去动态创建类了,直接贴代码. class BaseModel(Model): class Meta: database = _tb class_new ...
- win32 - 将线程重定向到另一个函数(附带Suspend的解释)
Suspend: 挂起指定的线程 备注:不要永远挂起线程, 因为在Win32中,进程堆是线程安全的对象,并且由于在不访问堆的情况下很难在Win32中完成很多工作,因此在Win32中挂起线程极有可能使进 ...
- Android加载PDF方案(pdf.js,支持缩放)
都知道,Android本身的webview是不支持pdf加载的(比不上iOS的webview,谁让人家NB呢),因此通过连接Google的一个服务器转换成功后返回给WebView显示.但是,但是,但是 ...
- postgresql中的json和jsonb
1. 简介postgresql9.3以后,我们可以将表的字段类型设置为json.同时,postgresql还提供了jsonb格式,jsonb是json的二进制形式.二者区别: postgresql9. ...
- MongoDB的安装及启动
下载地址 https://www.mongodb.com/try/download/community 安装步骤 自定义安装目录 配置环境 下面是你安装后的mongodb的目录 在电脑的环境变量Pat ...
- 【生成对抗网络学习 其二】GAN(keras实现)代码阅读笔记
想来想去还是记录一下吧,主要是怕以后时间长忘了 好记性不如烂笔头 代码来自eriklindernoren的开源GAN实现:https://github.com/eriklindernoren/Kera ...