是时候来唠一唠synchronized关键字了,Java多线程的必问考点!

简介: 本文简要介绍了Java中的`synchronized`关键字,它是用于保证多线程环境下的同步,解决原子性、可见性和顺序性问题。从JDK1.6开始,synchronized进行了优化,性能得到提升,现在仍可在项目中使用。synchronized有三种用法:修饰实例方法、静态方法和代码块。文章还讨论了synchronized修饰代码块的锁对象、静态与非静态方法调用的互斥性,以及构造方法不能被同步修饰。此外,通过反汇编展示了`synchronized`在方法和代码块上的底层实现,涉及ObjectMonitor和monitorenter/monitorexit指令。

写在开头

在之前的博文中,我们介绍了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参数如下:

image.png

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

【反汇编结果】

image.png

由上图可看出同步方法通过加 ACC_SYNCHRONIZED 标识实现线程的执行权的控制,如果修饰的是实例方法,JVM会获取对象锁,如果修饰的是静态方法,JVM会获取当前类锁。

2、当synchronized修饰代码块时

public class Test {
   
   
    public void method() {
   
   
        synchronized (this) {
   
   
            System.out.println("synchronized");
        }
    }
}

【反汇编结果】

image.png

与同步方法不同,同步代码块中使用了monitorenter 和 monitorexit 指令,其中 monitorenter 指令指向同步代码块的开始位置,monitorexit 指令则指明同步代码块的结束位置,并且monitorexit标识有2个,以保证在正常执行和异常情况下均可释放锁。

在命令执行到monitorenter时,线程会去尝试获取对象得锁,这里也可称之为对象所对应的monitor所有权。写到这里,我们又要做一个知识点扩展啦。

知识点扩展:

在JVM中monitor的底层基于C++实现,被称之为对象监视器,每个对象都会内置一个ObjectMonitor与之关联,关联的起始地址存于对象头的MarkWord中。

image.png

ObjectMonitor几个关键属性:

  • _owner:指向持有ObjectMonitor对象的线程
  • _WaitSet:存放处于wait状态的线程队列
  • EntryList:存放处于等待锁block状态的线程队列
  • recursions:锁的重入次数
  • _count:用来记录该线程获取锁的次数

当多个线程同时访问同步代码时,会被放入EntryList中,根据线程优先级尝试获取对象锁,如果锁的计数器为 0 则表示锁可以被获取,获取到锁的线程进入owner区域,count加1,这里其实还有之前说的object中的wait/notify/notifyall的组合,也依赖monitor,所以他们才必须用在同步方法或代码块中。
对象锁的的拥有者线程才可以执行 monitorexit 指令来释放锁。在执行 monitorexit 指令后,将锁计数器设为 0,表明锁被释放,其他线程可以尝试获取锁。

总结

关于synchronized的介绍其实远没有结束,还有很多细节可以值得学习,我们会在后面的文章中逐渐补充,避免文章过长,读者失去阅读的耐心!

结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

目录
相关文章
|
15天前
|
Java 数据库连接 API
互联网大厂校招 JAVA 工程师笔试题解析及常见考点分析
本文深入解析互联网大厂校招Java工程师笔试题,涵盖基础知识(数据类型、流程控制)、面向对象编程(类与对象、继承与多态)、数据结构与算法(数组、链表、排序算法)、异常处理、集合框架、Java 8+新特性(Lambda表达式、Stream API)、多线程与并发、IO与NIO、数据库操作(JDBC、ORM框架MyBatis)及Spring框架基础(IoC、DI、AOP)。通过技术方案讲解与实例演示,助你掌握核心考点,提升解题能力。
55 2
|
16天前
|
存储 安全 算法
Java 集合面试题 PDF 下载及高频考点解析
本文围绕Java集合面试题展开,详细解析了集合框架的基本概念、常见集合类的特点与应用场景。内容涵盖`ArrayList`与`LinkedList`的区别、`HashSet`与`TreeSet`的对比、`HashMap`与`ConcurrentHashMap`的线程安全性分析等。通过技术方案与应用实例,帮助读者深入理解集合类的特性和使用场景,提升解决实际开发问题的能力。文末附带资源链接,供进一步学习参考。
28 4
|
19天前
|
存储 安全 Java
Java 集合面试题从数据结构到 HashMap 源码剖析详解及长尾考点梳理
本文深入解析Java集合框架,涵盖基础概念、常见集合类型及HashMap的底层数据结构与源码实现。从Collection、Map到Iterator接口,逐一剖析其特性与应用场景。重点解读HashMap在JDK1.7与1.8中的数据结构演变,包括数组+链表+红黑树优化,以及put方法和扩容机制的实现细节。结合订单管理与用户权限管理等实际案例,展示集合框架的应用价值,助你全面掌握相关知识,轻松应对面试与开发需求。
75 3
|
23天前
|
算法 Java 调度
Java多线程基础
本文主要讲解多线程相关知识,分为两部分。第一部分涵盖多线程概念(并发与并行、进程与线程)、Java程序运行原理(JVM启动多线程特性)、实现多线程的两种方式(继承Thread类与实现Runnable接口)及其区别。第二部分涉及线程同步(同步锁的应用场景与代码示例)及线程间通信(wait()与notify()方法的使用)。通过多个Demo代码实例,深入浅出地解析多线程的核心知识点,帮助读者掌握其实现与应用技巧。
|
2月前
|
Java 中间件 调度
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
本文涉及InheritableThreadLocal和TTL,从源码的角度,分别分析它们是怎么实现父子线程传递的。建议先了解ThreadLocal。
116 4
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
|
1月前
|
Java
java 多线程异常处理
本文介绍了Java中ThreadGroup的异常处理机制,重点讲解UncaughtExceptionHandler的使用。通过示例代码展示了当线程的run()方法抛出未捕获异常时,JVM如何依次查找并调用线程的异常处理器、线程组的uncaughtException方法或默认异常处理器。文章还提供了具体代码和输出结果,帮助理解不同处理器的优先级与执行逻辑。
|
1月前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
76 0
|
3月前
|
存储 缓存 人工智能
【原理】【Java并发】【synchronized】适合中学者体质的synchronized原理
本文深入解析了Java中`synchronized`关键字的底层原理,从代码块与方法修饰的区别到锁升级机制,内容详尽。通过`monitorenter`和`monitorexit`指令,阐述了`synchronized`实现原子性、有序性和可见性的原理。同时,详细分析了锁升级流程:无锁 → 偏向锁 → 轻量级锁 → 重量级锁,结合对象头`MarkWord`的变化,揭示JVM优化锁性能的策略。此外,还探讨了Monitor的内部结构及线程竞争锁的过程,并介绍了锁消除与锁粗化等优化手段。最后,结合实际案例,帮助读者全面理解`synchronized`在并发编程中的作用与细节。
187 8
【原理】【Java并发】【synchronized】适合中学者体质的synchronized原理
|
3月前
|
缓存 安全 Java
【Java并发】【synchronized】适合初学者体质入门的synchronized
欢迎来到我的Java线程同步入门指南!我不是外包员工,梦想是写高端CRUD。2025年我正在沉淀中,博客更新速度加快,欢迎点赞、收藏、关注。 本文介绍Java中的`synchronized`关键字,适合初学者。`synchronized`用于确保多个线程访问共享资源时不会发生冲突,避免竞态条件、保证内存可见性、防止原子性破坏及协调多线程有序访问。
109 8
【Java并发】【synchronized】适合初学者体质入门的synchronized
|
3月前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
145 23