Java并发编程必备之Synchronized关键字深入解析

2025-04-03 03:50

本文主要是介绍Java并发编程必备之Synchronized关键字深入解析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种...

一、前言

Java多线程编程中,线程安全是非常重要的一个概念。为了防止多个线程同时访问共享资源时出现数据不一致或其他竞态条件问题,Java提供了synchronized关键字和监视器锁(Monitor Lock)机制。这个博客将深入探讨synchronized的原理、使用场景以及与监视器锁的关系。

二、Synchronized关键字pLHhVhbSQE

synchronized是Java中的一个关键字,用于在多线程环境中控制对共享资源的访问。当一个线程执行synchronized修饰的方法或代码块时,其他线程将无法访问相同的资源,直到当前线程释放资源锁。

2.1 Synchronized的特性

1. 互斥

Synchronized 会起到互斥效果, 某个线程执行到某个对象的 synchronized 中时, 其他线程如果也执行到同一个对象 synchronized 就会阻塞等待。

• 进入 synchronized 修饰的代码块, 相当于 加锁
• 退出 synchronized 修饰的代码块, 相当于 解锁

   synchronized (locker){
                for (int i = 0; i < 5000; i++) {
                    count++;
                }
            }

Java并发编程必备之Synchronized关键字深入解析

synchronized用的锁是存在Java对象头里的。

Java并发编程必备之Synchronized关键字深入解析

可以粗略理解成, 每个对象在内存中存储的时候, 都存有一块内存表示当前的 “锁定” 状态(类似于厕所
的 “有人/无人”)。
如果当前是 “无人” 状态, 那么就可以使用, 使用时需要设为 “有人” 状态。
如果当前是 “有人” 状态, 那么其他人无法使用, 只能排队。

Java并发编程必备之Synchronized关键字深入解析

理解 “阻塞等待”。
针对每⼀把锁, 操作系统内部都维护了⼀个等待队列. 当这个锁被某个线程占有的时候, 其他线程尝试
进行加锁, 就加不上了, 就会阻塞等待, ⼀直等到之前的线程解锁之后, 由操作系统唤醒⼀个新的线程,
再来获取到这个锁.

注意:

• 上一个线程解锁之后, 下一个线程并不是立即就能获取到锁。而是要靠操作系统来 “唤醒”. 这也就
是操作系统线程调度的一部分工作。
• 假设有 A B C 三个线程, 线程 A 先获取到锁, 然后 B 尝试获取锁, 然后 C 再尝试获取锁, 此时 B 和 C
都在阻塞队列中排队等待.。但是当 A 释放锁之后, 虽然 B 比 C 先来的, 但是 B 不一定就能获取到锁,
而是和 C 重新竞争, 并不遵守先来后到的规则。
synchronized的底层是使用操作系统的mutex lock实现的。

2. 可重入

synchronized 同步块对同⼀条线程来说是可重入的,不会出现自己把自己锁死的问题。

理解 “把自己锁死”

⼀个线程没有释放锁, 然后又尝试再次加锁。
第⼀次加锁, 加锁成功
lock();
第⼆次加锁, 锁已经被占用, 阻塞等待
lock();
按照之前对于锁的设定, 第二次加锁的时候, 就会阻塞等待. 直到第一次的锁被释放, 才能获取到第二
个锁.。但是释放第⼀个锁也是由该线程来完成, 结果这个线程已经躺平了, 啥都不想干了, 也就无法进行解锁操作. 这时候就会死锁。

Java并发编程必备之Synchronized关键字深入解析

Java 中的 synchronized可重⼊锁, 因此没有上面的问题。

for (int i = 0; i < 50000; i++) {
 synchronized (locker) {
 synchronized (locker) {
             count++;
           }
      }
}

在可重入锁的内部, 包含了 “线程持有者” 和 “计数器” 两个信息。
• 如果某个线程加锁的时候, 发现锁已经被人占用, 但是恰好占用的正是自己, 那么仍然可以继续获取到锁, 并让计数器自增。
• 解锁的时候计数器递减为 php0 的时候, 才真正释放锁. (才能被别的线程获取到)

三、synchronized 的使用示例

synchronized 本质上要修改指定对象的 “对象头”。从使用角度来看,synchronized 也势必要搭配⼀个具体的对象来使用。

3.1 修饰代码块: 明确指定锁哪个对象

1.锁任意对象

public class Synchrinized1 {
    private static int count;
    public static void main(String[] args) {
        Object locker=new Object();
        Thread t1=new Thread(()->{
            for (int i = 0; i <10000 ; i++) {
                synchronized (locker){
                    count++;//锁任意对象
                }
            }
        });
    }
}

2.锁当前对象

public class Synchrinized1 {
    private static int count;
    public static void main(String[] args) {
        Object locker=new Object();
        Thread t1=new Thread(()->{
            for (int i = 0; i <10000 ; i++) {
                synchronized (this){
                    count++;//锁当前对象
                }
            }
        });
    }
}

3.2直接修饰普通方法

pujavascriptblic class Synchronized2{
    public int count=0;
    synchronized public void methond(){
        count++;
    }
}

3.3 修饰静态方法

public class Synchronized{
    public static int count=0;
   public static synchronized void methon(){
        count++;
    }
}

四 、监视器锁(Monitor Lock)

Monitor Lock(监视器锁)是一种高级同步机制,底层实现了synchronized关键字的功能。每个对象都关联一个监视器锁,当线程进入synchronized块时,线程会获取该对象的监视器锁。

4.1 监视器锁的特点

  • 互斥性:同一时间只有一个线程可以持有监视器锁。
  • 可重入性:同一线程可以多次获取已持有的监视器锁。
  • 锁升级:从未锁定状态偏向锁,再到轻量级锁,最终升级为重量级锁

4.2 Synchronized的底层实现

synchronized关键字的实现依赖于JVM,而JVM底层使用了监视器锁(Monitor Lock)来实现线程的同步。

当线程执行synchronized代码时,JVM会检查是否已经获得了监视器锁:

  • 如果未加锁,当前线程会尝试获取锁。
  • 如果已加锁,当前线程将被阻塞,直到锁被释放。

4.3 onitor Lock的状态

一个监视器锁有以下四种状态:

  • 未锁定状态:任何线程都可以竞争锁。
  • 偏向锁状态:只允许一个线程持有锁,减少竞争。
  • 轻量级锁状态:多个线程竞争锁,但未发生旋转或阻塞。
  • 重量级锁状态:线程竞争激烈,锁被线程阻塞等待。

4.4 Synchronized与ReentrantLock的比较

4.5 常见问题及解决方案

1. 避免过度同步

过度http://www.chinasem.cn同步会降低并发性能,应尽量缩小同步范围。

2. 避免死锁

死锁是由于多个线程互相持有对方资源而导致的。可以通过如下方式避免:

  • 按顺序加锁:对多个锁的加锁顺序进行统一。
  • 避免嵌套锁:减少嵌套使用锁的场景。

3. 数据一致性问题

在单例模式或共享变量的场景中,必须确保所有修改共享资源的操作都在同步块内。

五、总结

通过本文,我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性。文章详细介绍了Synchronized的三种使用方式:修饰代码块、修饰普通方法和修饰静态方法。同时,我们还解析了监视器锁(Monitor Lock)的底层实现,以及SynchronizedReentrantLock的对比。最后,文章总结了并发编程中常见问题的解决方案,如避免过度同步、防止死锁和保持数据一致性。本文旨在帮助开发者更好地理解和使用Synchronized,从而编写出高效、安全的并发程序。

到此这篇关于Java并发编程必备之Synchronized关键字深入解析的文章就介绍到这了,更多相关Java Synchronized关键字内容请搜索China编程(whttp://www.chinasem.cnww.chinasem.cn)以前的文章或继续浏览下面的相关文章希望大家以后多多支持China编程(www.chinasem.cn)!

这篇关于Java并发编程必备之Synchronized关键字深入解析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1154068

相关文章

Spring Security简介、使用与最佳实践

《SpringSecurity简介、使用与最佳实践》SpringSecurity是一个能够为基于Spring的企业应用系统提供声明式的安全访问控制解决方案的安全框架,本文给大家介绍SpringSec... 目录一、如何理解 Spring Security?—— 核心思想二、如何在 Java 项目中使用?——

SpringBoot+RustFS 实现文件切片极速上传的实例代码

《SpringBoot+RustFS实现文件切片极速上传的实例代码》本文介绍利用SpringBoot和RustFS构建高性能文件切片上传系统,实现大文件秒传、断点续传和分片上传等功能,具有一定的参考... 目录一、为什么选择 RustFS + SpringBoot?二、环境准备与部署2.1 安装 RustF

springboot中使用okhttp3的小结

《springboot中使用okhttp3的小结》OkHttp3是一个JavaHTTP客户端,可以处理各种请求类型,比如GET、POST、PUT等,并且支持高效的HTTP连接池、请求和响应缓存、以及异... 在 Spring Boot 项目中使用 OkHttp3 进行 HTTP 请求是一个高效且流行的方式。

MySQL的JDBC编程详解

《MySQL的JDBC编程详解》:本文主要介绍MySQL的JDBC编程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、前置知识1. 引入依赖2. 认识 url二、JDBC 操作流程1. JDBC 的写操作2. JDBC 的读操作总结前言本文介绍了mysq

java.sql.SQLTransientConnectionException连接超时异常原因及解决方案

《java.sql.SQLTransientConnectionException连接超时异常原因及解决方案》:本文主要介绍java.sql.SQLTransientConnectionExcep... 目录一、引言二、异常信息分析三、可能的原因3.1 连接池配置不合理3.2 数据库负载过高3.3 连接泄漏

javacv依赖太大导致jar包也大的解决办法

《javacv依赖太大导致jar包也大的解决办法》随着项目的复杂度和依赖关系的增加,打包后的JAR包可能会变得很大,:本文主要介绍javacv依赖太大导致jar包也大的解决办法,文中通过代码介绍的... 目录前言1.检查依赖2.更改依赖3.检查副依赖总结 前言最近在写项目时,用到了Javacv里的获取视频

Java实现字节字符转bcd编码

《Java实现字节字符转bcd编码》BCD是一种将十进制数字编码为二进制的表示方式,常用于数字显示和存储,本文将介绍如何在Java中实现字节字符转BCD码的过程,需要的小伙伴可以了解下... 目录前言BCD码是什么Java实现字节转bcd编码方法补充总结前言BCD码(Binary-Coded Decima

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分