深入理解Java中synchronized三种使用方式:助您写出线程安全的代码

简介: `synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。

添加图片注释,不超过 140 字(可选)


一、概念

synchronized 是一种内置的 Java 关键字,它用于实现线程的同步。当一个线程进入synchronized块或方法时,它获得了锁,这会阻止其他线程同时进入相同的synchronized块或方法,从而确保了共享资源的互斥访问。

synchronized 是 Java 中用于实现线程同步的关键字。它提供了一种独占锁的机制,用于确保多个线程之间的互斥访问共享资源。以下是关于synchronized的更详细解释:

使用方法:

synchronized关键字在Java中有三种主要的使用方式:


添加图片注释,不超过 140 字(可选)

1.修饰非静态方法(锁class的同一实例的此方法)

当您在实例的非静态方法上使用synchronized关键字时,它会将该方法变成同步方法,相当于对当前实例对象(this)加锁,this作为对象监视器。这意味着只有一个线程可以同时执行该实例方法,以确保对该实例的互斥访问。 当前类会创建多个实例对象,synchronized独立的控制每个实例对象的同步。

public synchronized void synchronizedInstanceMethod() {     // 同步的代码块 }

2. 修饰静态方法(锁class的所有实例的此方法)

在静态方法上使用synchronized关键字时,它会将该方法变为同步静态方法,相当于对当前类的Class对象加锁,当前类的Class对象作为对象监视器。这意味着只有一个线程可以同时执行该静态方法,以确保对该类的互斥访问。 当前类会创建多个实例对象,所以实例对应同一个静态方法,所以synchronized控制所以实例对象的同步。锁定的是类的 Class 对象,因此它会阻止不同实例以及静态方法之间的并发执行,因为它们共享相同的 Class 对象。

public static synchronized void synchronizedStaticMethod() {     // 同步的代码块 }

3.修饰代码块(锁定特定的对象)

可以使用synchronized关键字来创建同步代码块,这样可以指定要加锁的对象,括号中括起来的对象就是对象监视器。这允许更细粒度的控制,可以选择对某个特定对象进行同步,而不是整个方法或类。

方式一:

当你使用 synchronized(class) 时,你锁定的是整个类的对象,而不是实例对象。这意味着无论多少实例对象存在,它们都会竞争同一个锁。

Object lock = new Object(); synchronized (lock) {     // 同步的代码块 }

方式二:

当你使用 synchronized(this) 时,你锁定的是当前实例对象(this)。这意味着同一实例的不同方法调用会相互排斥,但不同实例之间的方法调用不会相互排斥。

synchronized (this) {     // 同步的代码块 }


二、实现原理

1、实现原理

synchronized底层原理是基于JVM的指令和对象的监视器(monitor)来实现的。synchronized可以修饰方法或者代码块,用来保证线程的同步和安全。

当一个线程要执行一个被synchronized修饰的方法或代码块时,它需要先获取该方法或代码块所属对象的监视器。如果获取成功,那么该线程就可以执行同步代码,并且监视器的计数器加一。如果获取失败,那么该线程就会阻塞,直到监视器被释放。

当一个线程执行完同步代码后,它会释放监视器,并且监视器的计数器减一。如果计数器为零,那么说明没有线程持有该监视器,其他线程就可以竞争获取该监视器。

synchronized修饰方法时,在字节码层面会有一个ACC_SYNCHRONIZED标志,用来表示该方法是同步的。synchronized修饰代码块时,在字节码层面会有monitorenter和monitorexit两个指令,分别用来进入和退出监视器。

synchronized 的底层实现原理可以概括为以下几点:

  • synchronized 通过监视器锁来实现线程同步。
  • 每个 Java 对象都有一个监视器锁。
  • 线程在获取了对象的监视器锁后,可以执行被修饰的代码。
  • 线程在释放了对象的监视器锁后,其他线程可以尝试获取监视器锁。

2、JDK底层的优化

synchronized在JDK1.6之后进行了优化,引入了偏向锁,轻量级锁,自旋锁等概念,用来提高性能和减少阻塞开销。以下是一些常见的优化详细说明:

  1. 偏向锁:JDK引入了偏向锁,它会将锁定的对象与线程相关联,当一个线程获得锁时,它会标记对象为已偏向该线程,以后再次进入同步块时,不需要竞争锁,而是直接获得。这对于减少无竞争情况下的锁开销非常有用。
  2. 轻量级锁:在低竞争情况下,JDK使用轻量级锁来减小锁开销。轻量级锁采用自旋方式来等待锁的释放,而不是进入阻塞状态。
  3. 自旋锁:当轻量级锁尝试获取锁失败时,JDK可以选择使用自旋锁。自旋锁不会使线程进入阻塞状态,而是一直尝试获取锁,通常在短时间内完成。这对于低竞争锁非常有用。
  4. 适应性自旋:JDK中的锁可以根据历史性能数据来调整自旋等待的次数,以达到更好的性能。

这些优化措施有助于提高synchronized的性能,使其在不同的竞争场景中更加高效。但请注意,优化是基于JVM和硬件平台的,因此在不同的环境中表现可能会有所不同。

三、相关题目

问题1:synchronized 是什么?它的作用是什么?

答案:synchronized 是Java中的关键字,用于实现多线程同步。它的主要作用是确保在同一时刻只有一个线程可以访问被synchronized修饰的代码块或方法,以避免多线程之间的竞态条件和数据不一致问题。

问题2:synchronized 有哪些用法?

答案:synchronized 可以用于以下用法:

  • 同步非静态实例方法:synchronized 修饰非静态方法,锁定的是实例对象。
  • 同步静态方法:synchronized 修饰静态方法,锁定的是类对象。
  • 同步代码块:synchronized 修饰代码块,可以手动指定锁对象,灵活控制同步范围。

问题3:什么是锁对象?

答案:锁对象是在synchronized代码块或方法中用于实现同步的对象。对于同步实例方法,锁对象是实例对象本身(this),而对于同步静态方法,锁对象是类的Class对象。对于同步代码块,你可以手动指定锁对象。

问题4:什么是重入锁?

答案:重入锁是指当一个线程持有锁时,它可以多次进入被锁定的代码块或方法而不会被阻塞。Java中的synchronized是可重入锁的一个例子,这意味着同一线程可以多次获取同一个锁,而不会造成死锁。

问题5:什么是偏向锁?

答案:偏向锁是一种优化,用于减少锁竞争的开销。它允许线程在没有竞争的情况下快速获得锁,而不必像重量级锁那样进入阻塞状态。只有在多线程竞争锁时,偏向锁才会升级为重量级锁。

问题6:什么是自旋锁?

答案:自旋锁是一种轻量级锁,它在尝试获取锁时不会立即阻塞线程,而是会在循环中不断尝试获取锁。这可以减少线程阻塞和恢复的开销,但如果锁竞争激烈,自旋锁可能会浪费大量CPU时间。

问题7:什么是锁粗化?

答案:锁粗化是一种优化,它将多个连续的synchronized块合并成一个更大的同步块,减少了锁操作的开销。这可以避免频繁地获取和释放锁,提高性能。

问题8:什么是锁消除?

答案:锁消除是一种优化,它通过静态分析来检测某些锁是不必要的,然后将其删除,以提高程序的性能。这通常发生在编译器级别。

问题9:synchronized 与 volatile 有什么区别?

答案:synchronized 用于实现多线程同步,确保线程之间的可见性和原子性。volatile 用于确保变量的可见性,但不能实现原子性。synchronized 具有更广泛的用途,而 volatile 主要用于标记变量以确保可见性。


目录
相关文章
|
23天前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
1月前
|
前端开发 Java
java实现队列数据结构代码详解
本文详细解析了Java中队列数据结构的实现,包括队列的基本概念、应用场景及代码实现。队列是一种遵循“先进先出”原则的线性结构,支持在队尾插入和队头删除操作。文章介绍了顺序队列与链式队列,并重点分析了循环队列的实现方式以解决溢出问题。通过具体代码示例(如`enqueue`入队和`dequeue`出队),展示了队列的操作逻辑,帮助读者深入理解其工作机制。
|
1月前
|
Java 中间件 调度
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
本文涉及InheritableThreadLocal和TTL,从源码的角度,分别分析它们是怎么实现父子线程传递的。建议先了解ThreadLocal。
77 4
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
|
18天前
|
Java
java 多线程异常处理
本文介绍了Java中ThreadGroup的异常处理机制,重点讲解UncaughtExceptionHandler的使用。通过示例代码展示了当线程的run()方法抛出未捕获异常时,JVM如何依次查找并调用线程的异常处理器、线程组的uncaughtException方法或默认异常处理器。文章还提供了具体代码和输出结果,帮助理解不同处理器的优先级与执行逻辑。
|
4天前
|
机器学习/深度学习 消息中间件 存储
【高薪程序员必看】万字长文拆解Java并发编程!(9-2):并发工具-线程池
🌟 ​大家好,我是摘星!​ 🌟今天为大家带来的是并发编程中的强力并发工具-线程池,废话不多说让我们直接开始。
25 0
|
2月前
|
存储 缓存 人工智能
【原理】【Java并发】【synchronized】适合中学者体质的synchronized原理
本文深入解析了Java中`synchronized`关键字的底层原理,从代码块与方法修饰的区别到锁升级机制,内容详尽。通过`monitorenter`和`monitorexit`指令,阐述了`synchronized`实现原子性、有序性和可见性的原理。同时,详细分析了锁升级流程:无锁 → 偏向锁 → 轻量级锁 → 重量级锁,结合对象头`MarkWord`的变化,揭示JVM优化锁性能的策略。此外,还探讨了Monitor的内部结构及线程竞争锁的过程,并介绍了锁消除与锁粗化等优化手段。最后,结合实际案例,帮助读者全面理解`synchronized`在并发编程中的作用与细节。
142 8
【原理】【Java并发】【synchronized】适合中学者体质的synchronized原理
|
2月前
|
缓存 安全 Java
【Java并发】【synchronized】适合初学者体质入门的synchronized
欢迎来到我的Java线程同步入门指南!我不是外包员工,梦想是写高端CRUD。2025年我正在沉淀中,博客更新速度加快,欢迎点赞、收藏、关注。 本文介绍Java中的`synchronized`关键字,适合初学者。`synchronized`用于确保多个线程访问共享资源时不会发生冲突,避免竞态条件、保证内存可见性、防止原子性破坏及协调多线程有序访问。
85 8
【Java并发】【synchronized】适合初学者体质入门的synchronized
|
2月前
|
消息中间件 Java 应用服务中间件
JVM实战—1.Java代码的运行原理
本文介绍了Java代码的运行机制、JVM类加载机制、JVM内存区域及其作用、垃圾回收机制,并汇总了一些常见问题。
JVM实战—1.Java代码的运行原理
|
2月前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
120 23
|
1月前
|
数据采集 存储 网络协议
Java HttpClient 多线程爬虫优化方案
Java HttpClient 多线程爬虫优化方案
OSZAR »