写在开头

在之前的博文中,我们介绍了volatile关键字,Java中的锁以及锁的分类,今天我们花5分钟时间,一起学习一下另一个关键字:synchronized

synchronized是什么?

首先synchronized是Java中的一个关键字,所谓关键字,就是Java中根据底层封装所赋予的一种具有特殊语义的单词,而synchronized译为同步之意,可保证在同一时刻,被它修饰的方法或代码块只能有一个线程执行,它的使用解决了并发多线程中的三大问题:原子性、可见性、顺序性

很多小伙伴在过往的书籍中可能会看到说synchronized是一种重量级锁,性能差,不建议在代码中使用,其实这是早期的synchronized特点,自JDK1.6之后,synchronized 引入了大量的优化如自旋锁、适应性自旋锁、锁消除、锁粗化、偏向锁、轻量级锁等技术来减少锁操作的开销,这些优化让 synchronized 锁的效率提升了很多。因此, synchronized 还是可以在实际项目中使用的,像 JDK 源码、很多开源框架都大量使用了 synchronized 。

synchronized的使用

synchronized在Java中主要的3种使用方式:

  1. 修饰实例方法: 为当前对象实例加锁,进入同步方法需要先获取对象锁;
  2. 修饰静态方法: 为当前类加锁,锁定的是Class对象,进入同步方法需要先获取类锁;
  3. 修饰代码块: 为指定对象加锁,进入同步方法需要先获取指定对象的锁。

样例:

//修饰实例方法,为当前实例加锁
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多线程的必问考点!的更多相关文章

  1. Java多线程同步 synchronized 关键字的使用

    代表这个方法加锁,相当于不管哪一个线程A每次运行到这个方法时,都要检查有没有其它正在用这个方法的线程B(或者C D等),有的话要等正在使用这个方法的线程B(或者C D)运行完这个方法后再运行此线程A, ...

  2. 【转】Java多线程编程中易混淆的3个关键字( volatile、ThreadLocal、synchronized)总结

    概述 最近在看<ThinKing In Java>,看到多线程章节时觉得有一些概念比较容易混淆有必要总结一下,虽然都不是新的东西,不过还是蛮重要,很基本的,在开发或阅读源码中经常会遇到,在 ...

  3. Java多线程(三)—— synchronized关键字详解

    一.多线程的同步 1.为什么要引入同步机制 在多线程环境中,可能会有两个甚至更多的线程试图同时访问一个有限的资源.必须对这种潜在资源冲突进行预防. 解决方法:在线程使用一个资源时为其加锁即可. 访问资 ...

  4. Java多线程学习(二)synchronized关键字(2)

    转载请备注地址:https://blog.csdn.net/qq_34337272/article/details/79670775 系列文章传送门: Java多线程学习(一)Java多线程入门 Ja ...

  5. Java多线程学习(二)synchronized关键字(1)

    转载请备注地址: https://blog.csdn.net/qq_34337272/article/details/79655194 Java多线程学习(二)将分为两篇文章介绍synchronize ...

  6. Java:synchronized关键字引出的多种锁

    前言 Java 中的 synchronized关键字可以在多线程环境下用来作为线程安全的同步锁.本文不讨论 synchronized 的具体使用,而是研究下synchronized底层的锁机制,以及这 ...

  7. 用代码说话:synchronized关键字和多线程访问同步方法的7种情况

    synchronized关键字在多线程并发编程中一直是元老级角色的存在,是学习并发编程中必须面对的坎,也是走向Java高级开发的必经之路. 一.synchronized性质 synchronized是 ...

  8. synchronized关键字的使用

    synchronized关键字是java并发编程中常使用的同步锁,用于锁住方法或者代码块,锁代码块时可以是synchronized(this){}.synchronized(Object){}.syn ...

  9. java线程基础巩固---数据同步引入并结合jconsole,jstack以及汇编指令认识synchronized关键字

    对于多线程编程而言其实老生成谈的就是数据同步问题,接下来就会开始接触这块的东东,比较麻烦,但是也是非常重要,所以按部就班的一点点去专研它,下面开始. 数据同步引入: 这里用之前写过的银行叫号的功能做为 ...

  10. java多线程——同步块synchronized详解

    Java 同步块(synchronized block)用来标记方法或者代码块是同步的.Java同步块用来避免竞争.本文介绍以下内容: Java同步关键字(synchronzied) 实例方法同步 静 ...

随机推荐

  1. Power BI 1 DAY

    目录 Power BI(商业智能)分析 BI 分析步骤 Power Query 表数据结构 区别 主键 Power Query中的纵向合并与横向合并 销售一表和销售二表进行纵向合并为一张销售表. 产品 ...

  2. NC22594 Rinne Loves Graph

    题目链接 题目 题目描述 Island 发生了一场暴乱!现在 Rinne 要和 Setsuna 立马到地上世界去. 众所周知:Island 是有一些奇怪的城镇和道路构成的(题目需要,游戏党勿喷),有些 ...

  3. python 中异常类型总结

    异常类型: 异常名称 描述BaseException             所有异常的基类SystemExit                   解释器请求退出KeyboardInterrupt  ...

  4. Git将某个文件合并到指定分支

    企业开发中,经常会单独拉分支去做自己的需求开发,但是某些时候一些公共的配置我们需要从主线pull,这时候整个分支merge显然不合适 1.切换至待合并文件的分支 git checkout <br ...

  5. python 创建动态类

    一般情况下多数是预先定义类 而少数特殊情况就需要去动态创建类了,直接贴代码. class BaseModel(Model): class Meta: database = _tb class_new ...

  6. win32 - 将线程重定向到另一个函数(附带Suspend的解释)

    Suspend: 挂起指定的线程 备注:不要永远挂起线程, 因为在Win32中,进程堆是线程安全的对象,并且由于在不访问堆的情况下很难在Win32中完成很多工作,因此在Win32中挂起线程极有可能使进 ...

  7. Android加载PDF方案(pdf.js,支持缩放)

    都知道,Android本身的webview是不支持pdf加载的(比不上iOS的webview,谁让人家NB呢),因此通过连接Google的一个服务器转换成功后返回给WebView显示.但是,但是,但是 ...

  8. postgresql中的json和jsonb

    1. 简介postgresql9.3以后,我们可以将表的字段类型设置为json.同时,postgresql还提供了jsonb格式,jsonb是json的二进制形式.二者区别: postgresql9. ...

  9. MongoDB的安装及启动

    下载地址 https://www.mongodb.com/try/download/community 安装步骤 自定义安装目录 配置环境 下面是你安装后的mongodb的目录 在电脑的环境变量Pat ...

  10. 【生成对抗网络学习 其二】GAN(keras实现)代码阅读笔记

    想来想去还是记录一下吧,主要是怕以后时间长忘了 好记性不如烂笔头 代码来自eriklindernoren的开源GAN实现:https://github.com/eriklindernoren/Kera ...