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

相关文章

java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)

《java中pdf模版填充表单踩坑实战记录(itextPdf、openPdf、pdfbox)》:本文主要介绍java中pdf模版填充表单踩坑的相关资料,OpenPDF、iText、PDFBox是三... 目录准备Pdf模版方法1:itextpdf7填充表单(1)加入依赖(2)代码(3)遇到的问题方法2:pd

Java Stream流之GroupBy的用法及应用场景

《JavaStream流之GroupBy的用法及应用场景》本教程将详细介绍如何在Java中使用Stream流的groupby方法,包括基本用法和一些常见的实际应用场景,感兴趣的朋友一起看看吧... 目录Java Stream流之GroupBy的用法1. 前言2. 基础概念什么是 GroupBy?Stream

Mysql中设计数据表的过程解析

《Mysql中设计数据表的过程解析》数据库约束通过NOTNULL、UNIQUE、DEFAULT、主键和外键等规则保障数据完整性,自动校验数据,减少人工错误,提升数据一致性和业务逻辑严谨性,本文介绍My... 目录1.引言2.NOT NULL——制定某列不可以存储NULL值2.UNIQUE——保证某一列的每一

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

SpringBoot监控API请求耗时的6中解决解决方案

《SpringBoot监控API请求耗时的6中解决解决方案》本文介绍SpringBoot中记录API请求耗时的6种方案,包括手动埋点、AOP切面、拦截器、Filter、事件监听、Micrometer+... 目录1. 简介2.实战案例2.1 手动记录2.2 自定义AOP记录2.3 拦截器技术2.4 使用Fi

最新Spring Security的基于内存用户认证方式

《最新SpringSecurity的基于内存用户认证方式》本文讲解SpringSecurity内存认证配置,适用于开发、测试等场景,通过代码创建用户及权限管理,支持密码加密,虽简单但不持久化,生产环... 目录1. 前言2. 因何选择内存认证?3. 基础配置实战❶ 创建Spring Security配置文件

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

C#中lock关键字的使用小结

《C#中lock关键字的使用小结》在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时,其他线程无法访问同一实例的该代码块,下面就来介绍一下lock关键字的使用... 目录使用方式工作原理注意事项示例代码为什么不能lock值类型在C#中,lock关键字用于确保当一个线程位于给定实例的代码块中时

springboot自定义注解RateLimiter限流注解技术文档详解

《springboot自定义注解RateLimiter限流注解技术文档详解》文章介绍了限流技术的概念、作用及实现方式,通过SpringAOP拦截方法、缓存存储计数器,结合注解、枚举、异常类等核心组件,... 目录什么是限流系统架构核心组件详解1. 限流注解 (@RateLimiter)2. 限流类型枚举 (

Java Thread中join方法使用举例详解

《JavaThread中join方法使用举例详解》JavaThread中join()方法主要是让调用改方法的thread完成run方法里面的东西后,在执行join()方法后面的代码,这篇文章主要介绍... 目录前言1.join()方法的定义和作用2.join()方法的三个重载版本3.join()方法的工作原