查漏补缺第二期(synchronized & 锁升级)

简介: 前言目前正在出一个查漏补缺专题系列教程, 篇幅会较多, 喜欢的话,给个关注❤️ ~本专题主要以Java语言为主, 好了, 废话不多说直接开整吧~Q1 & 请说说对synchronized的理解在Java中,关键字synchronized用于实现多线程之间的同步。它可以应用于方法或代码块,并确保在同一时间只有一个线程可以访问被同步的代码段.

前言

目前正在出一个查漏补缺专题系列教程, 篇幅会较多, 喜欢的话,给个关注❤️ ~

本专题主要以Java语言为主, 好了, 废话不多说直接开整吧~

Q1 & 请说说对synchronized的理解

Java中,关键字synchronized用于实现多线程之间的同步。它可以应用于方法或代码块,并确保在同一时间只有一个线程可以访问被同步的代码段.

  • 保证原子性:synchronized关键字用于对临界区代码进行加锁,确保多个线程无法同时执行被同步的代码块或方法。这样可以保证代码块或方法中的操作是原子的, 即要么全部执行完毕,要么不执行,从而避免了竞态条件(race condition)的问题。
  • 内置锁(Intrinsic Lock):在Java中,每个对象都有一个内置的锁,也称为监视器锁或互斥锁。当一个线程进入synchronized代码块时,它必须首先获得对象的内置锁,其他线程必须等待直到锁被释放。这样确保了同一时间只有一个线程能够执行被同步的代码块。
  • 互斥性和可见性:synchronized关键字不仅提供了互斥访问,还确保了可见性。当一个线程获取到锁时,它会清空工作内存中的变量副本,强制从主内存中重新加载变量的值。这样可以确保线程在访问变量时获取最新的值,而不是使用过期的副本。
  • 锁的粒度:
  • 对象锁:当使用synchronized修饰实例方法时,锁定的是当前对象实例。这意味着同一时间只有一个线程可以访问该实例的synchronized方法,不同的实例之间互不影响。
  • 类锁:当使用synchronized修饰静态方法时,锁定的是整个类对象。这意味着同一时间只有一个线程可以访问该类的任意一个synchronized静态方法,无论是不同实例还是相同实例。
  • 重入性:Java中的synchronized关键字支持重入性。也就是说,如果一个线程已经获得了一个对象的锁,它可以再次获取该对象的锁而不会被阻塞。这种机制允许线程在同步代码块内部调用其他同步方法,避免了自己阻塞自己的情况。

总之,Java中的synchronized关键字提供了一种简单而有效的方法来确保多线程之间的同步和线程安全性。它通过使用内置锁、互斥性和可见性保证了临界区代码的原子性执行,避免了数据竞争和数据不一致的问题。

下面我们通过一些例子复习一下:

  • 同步代码块(Synchronized Blocks):除了使用synchronized修饰整个方法外,还可以使用synchronized修饰一段代码块,称为同步代码块。这样可以在方法中只对某一部分代码进行同步,而不是整个方法。

public void someMethod() {
    // 非同步代码
    synchronized (this) {
        // 需要同步的代码
    }
    // 非同步代码
}

在上面的示例中,使用synchronized(this)将某个对象作为锁来同步代码块,只有一个线程可以进入同步代码块,其他线程需要等待。

  • 同步方法(Synchronized Methods):这是synchronized的常见用法,将关键字直接应用于方法上,以实现整个方法的同步。当一个线程进入同步方法时,它会获得方法所属对象的锁,其他线程需要等待。

public synchronized void someMethod() {
    // 需要同步的代码
}
  • 同步静态方法(Synchronized Static Methods):类级别的方法可以使用synchronized关键字进行同步。与同步方法类似,但是锁的范围是该方法所在的类对象。

public static synchronized void someMethod() {
    // 需要同步的代码
}
  • 同步块的对象锁(Object Lock of Synchronized Blocks):在同步代码块中,可以使用任意的对象作为锁,而不仅仅是this关键字。这使得我们可以更细粒度地控制同步。

public void someMethod() {
    Object lock = new Object();
    // 非同步代码
    synchronized (lock) {
        // 需要同步的代码
    }
    // 非同步代码
}
  • 同步锁的重入(Reentrant Synchronization):在Java中,一个线程在获得一个对象锁后,可以再次获得该对象的锁。这种重入机制可以避免线程死锁
public synchronized void methodA() {
    // 执行一些操作
    methodB(); // 可重入,可以再次获得锁
    // 执行一些操作
}
public synchronized void methodB() {
    // 执行一些操作
}

Q2 & synchronized锁升级有了解过吗,它是怎么样的一个过程?

锁升级是指在Java中,synchronized关键字在不同的情况下可以升级使用不同级别的锁,以提高性能和减少开销。Java中的锁升级主要涉及到偏向锁、轻量级锁和重量级锁这三个级别。

  • 偏向锁(Biased Locking): 偏向锁是指当只有一个线程访问同步块时,为了减少同步开销,JVM会自动将对象的锁记录下来,标记为偏向锁状态。当其他线程访问该同步块时,不需要竞争锁,而是直接获取锁。
  • 锁升级过程:
  • 初始状态:对象没有锁标记。
  • 偏向锁申请:当第一个线程访问同步块时,JVM将该线程ID记录在对象头部,并将对象的标记状态设置为偏向锁。
  • 偏向锁撤销:当其他线程尝试获取锁时,发现对象的偏向锁被占用,会撤销偏向锁,升级为轻量级锁。

public class BiasedLockExample {
    private static final Object lock = new Object();
    public static void main(String[] args) {
        synchronized (lock) {
            // 同步块
        }
    }
}
  • 轻量级锁(Lightweight Locking)轻量级锁是指当多个线程轻度竞争同步块时,JVM会将对象的锁记录在线程的栈帧中,而不是在对象头中。线程在进入同步块之前,通过CAS(比较并交换)操作尝试获取锁。如果CAS成功,则表示获取锁成功,进入同步块;如果CAS失败,表示存在竞争,升级为重量级锁。
  • 锁升级过程:
  • 初始状态:对象没有锁标记。
  • 轻量级锁申请:第一个线程进入同步块时,JVM将锁的记录信息复制到线程的栈帧中,并将对象的标记状态设置为轻量级锁
  • 轻量级锁竞争:当其他线程尝试获取锁时,会使用CAS操作来竞争锁。如果CAS成功,表示获取锁成功,进入同步块;如果CAS失败,表示存在竞争,升级为重量级锁
public class LightweightLockExample {
    private static final Object lock = new Object();
    public static void main(String[] args) {
        synchronized (lock) {
            // 同步块
        }
    }
}
  • 重量级锁(Heavyweight Locking): 重量级锁是指当多个线程激烈竞争同步块时,JVM会将对象的锁升级为重量级锁,使用操作系统提供的互斥量来实现锁机制。重量级锁涉及到线程的阻塞和唤醒操作,开销较大。
  • 锁升级过程:
  • 初始状态:对象没有锁标记。
  • 重量级锁申请:当多个线程轮流竞争同步块时,锁会直接升级为重量级锁,通过操作系统提供的互斥量来实现锁机制。

public class HeavyweightLockExample {
    private static final Object lock = new Object();
    public static void main(String[] args) {
        synchronized (lock) {
            // 同步块
        }
    }
}

需要注意的是,锁升级的过程是由JVM自动完成的,开发人员无需显式地控制锁升级。JVM会根据同步竞争的情况来自动选择合适的锁级别,以提供更好的性能和效率。

结束语

大家可以针对自己薄弱的地方进行复习, 然后多总结,形成自己的理解,不要去背~

本着把自己知道的都告诉大家,如果本文对您有所帮助,点赞+关注鼓励一下呗~

相关文章

项目源码(源码已更新 欢迎star⭐️)

往期设计模式相关文章

设计模式项目源码(源码已更新 欢迎star⭐️)

Kafka 专题学习

项目源码(源码已更新 欢迎star⭐️)

ElasticSearch 专题学习

项目源码(源码已更新 欢迎star⭐️)

往期并发编程内容推荐

推荐 SpringBoot & SpringCloud (源码已更新 欢迎star⭐️)

博客(阅读体验较佳)






















































相关文章
|
弹性计算 Cloud Native 5G
藏经阁2023年书籍推荐
好书一起看,技术干货学习就上藏经阁。藏经阁收录内容涵盖编程语言、云原生、数据库、大数据、AI等热门技术领域,让开发者们享受阅读优质内容。这次为您带来2023年4月至2024年1月的书籍推荐。
1487 5
|
NoSQL 关系型数据库 MySQL
多人同时导出 Excel 干崩服务器?怎样实现一个简单排队导出功能!
业务诉求:考虑到数据库数据日渐增多,导出会有全量数据的导出,多人同时导出可以会对服务性能造成影响,导出涉及到mysql查询的io操作,还涉及文件输入、输出流的io操作,所以对服务器的性能会影响的比较大;结合以上原因,对导出操作进行排队; 刚开始拿到这个需求,第一时间想到就是需要维护一个FIFO先进先出的队列,给定队列一个固定size,在队列里面的人进行排队进行数据导出,导出完成后立马出队列,下一个排队的人进行操作;还考虑到异步,可能还需要建个文件导出表,主要记录文件的导出情况,文件的存放地址,用户根据文件列表情况下载导出文件。
多人同时导出 Excel 干崩服务器?怎样实现一个简单排队导出功能!
|
10月前
|
人工智能 自然语言处理 搜索推荐
高性价比| OpenSearch 智能问答版开箱即用 DeepSeek-R1
OpenSearch LLM智能问答版基于DeepSeek-R1一分钟搭建RAG系统。
1691 11
高性价比| OpenSearch 智能问答版开箱即用 DeepSeek-R1
|
算法 数据可视化 定位技术
QGIS+Conda+jupyter玩转Python GIS
QGIS+Conda+jupyter玩转Python GIS
495 1
|
监控 PHP Apache
优化 PHP-FPM 参数配置:实现服务器性能提升
优化PHP-FPM的参数配置可以显著提高服务器的性能和稳定性。通过合理设置 `pm.max_children`、`pm.start_servers`、`pm.min_spare_servers`、`pm.max_spare_servers`和 `pm.max_requests`等参数,并结合监控和调优措施,可以有效应对高并发和负载波动,确保Web应用程序的高效运行。希望本文提供的优化建议和配置示例能够帮助您实现服务器性能的提升。
722 3
|
算法 程序员 开发工具
代码随想录 学习记录(1)
代码随想录 学习记录(1)
603 0
|
存储 NoSQL 安全
go 连接mongodb执行查询的代码
在Go语言中,你可以使用官方的MongoDB驱动程序 `"go.mongodb.org/mongo-driver/mongo"` 来连接MongoDB并执行查询。以下是一个简单的示例代码,演示如何连接MongoDB并执行查询: 首先,确保你已经安装了MongoDB驱动程序: ```bash go get go.mongodb.org/mongo-driver/mongo ``` 然后,可以使用以下示例代码: ```go package main import ( "context" "fmt" "log" "time" "go.mongodb.org/mongo-driv
336 0
|
iOS开发 MacOS Windows
Mac 去除自动生成.DS_Store文件
Mac 去除自动生成.DS_Store文件
889 0
|
XML Java 应用服务中间件
安装使用IDEA,修改样式,配置服务,构建Maven项目(超级详细版)
安装使用IDEA,修改样式,配置服务,构建Maven项目(超级详细版)
190 0
|
Java Maven 开发工具
Maven - 生成的 jar 包容量变大异常排查
maven 打包 jar 包容量异常原因排查与解决。
1286 0
Maven - 生成的 jar 包容量变大异常排查