深入理解Java中的synchronized关键字

目录

前言

一、什么是synchronized

二、synchronized的底层实现

三、synchronized与其他同步机制的比较

四、synchronized的使用方式

1. synchronized的重入

2.synchronized的异常


前言

        Java是一种面向对象的编程语言,以其强大的并发处理能力而闻名。在多线程编程中,保证线程安全是一个重要的问题。本文将详细探讨Java中的一个关键机制——synchronized关键字,帮助你深刻理解其概念、使用方法及其背后的工作原理。

一、什么是synchronized

   synchronized是Java提供的一种内置同步机制,用于解决多线程环境下的并发问题。当多个线程同时访问共享资源时,可能会出现数据不一致的情况。synchronized通过锁(monitor)的方式来确保同一时间只有一个线程可以访问被保护的代码块,从而避免数据的竞争问题。

二、synchronized的底层实现

        Java中synchronized关键字是通过进入和退出Monitor(监视器)对象来实现的。每个对象都有一个隐式的监视器锁。当我们使用synchronized同步方法或同步代码块时,线程必须先获得该锁才能进入同步区域。

        在JVM内部synchronized是通过字节码指令monitorentermonitorexit来实现的。当线程执行到synchronized区域时,它会尝试获取对象的监视器锁。若成功,则进入同步区域;否则,线程会被阻塞,直到获得锁为止。

三、synchronized与其他同步机制的比较

除了synchronized之外,Java还提供了其他一些高级的同步机制,如ReentrantLockSemaphore等。以下是它们的一些比较:

  • 可重入性synchronizedReentrantLock都是可重入锁。可重入性意味着同一个线程可以多次进入同步代码块而不会被自己阻塞。

  • 灵活性ReentrantLocksynchronized更加灵活。它提供了更多的方法,例如lockInterruptibly()tryLock()等,可以更精细地控制锁的行为。

  • 性能:在较早版本的Java中,synchronized的性能表现较差。但从Java 6开始,JVM对synchronized做了诸多优化,如偏向锁、轻量级锁以及自旋锁等,使其性能有了显著提升。

  • 条件变量ReentrantLock提供了Condition类,可以实现多个等待队列,配合await()signal()方法实现更加复杂的线程间通信。而synchronized只能依赖于wait()notify()notifyAll()方法来管理线程间的通信。

四、synchronized的使用方式

        关键字synchronized拥有锁重入的功能,也就是在使用synchronized时,当一个线程得到了一个对象的锁后,再次请求此对象时是可以再次得到该对象的锁。出现异常,锁自动释放。

1. synchronized的重入

package com.ctb.sync5;
/**
 * synchronized的重入
 * 
 * @author biao
 *
 * 2024年
 */
public class SyncDubbo1 {
​
    public synchronized void method1(){
        System.out.println("method1..");
        method2();
    }
    public synchronized void method2(){
        System.out.println("method2..");
        method3();
    }
    public synchronized void method3(){
        System.out.println("method3..");
    }
    
    public static void main(String[] args) {
        final SyncDubbo1 sd = new SyncDubbo1();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                sd.method1();
            }
        });
        t1.start();
    }
}

结果: 

注:synchronized的重入,在同时添加都synchronized锁后,在得到一个对象的锁后是可以再次得到该对象的锁的

package com.ctb.sync5;
/**
 * synchronized的重入
 * 
 * @author biao
 *
 * 2024年
 */
public class SyncDubbo2 {
​
    static class Main {
        public int i = 10;
        public synchronized void operationSup(){
            try {
                i--;
                System.out.println("Main print i = " + i);
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    static class Sub extends Main {
        public synchronized void operationSub(){
            try {
                while(i > 0) {
                    i--;
                    System.out.println("Sub print i = " + i);
                    Thread.sleep(100);      
                    this.operationSup();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    public static void main(String[] args) {
        
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                Sub sub = new Sub();
                sub.operationSub();
            }
        });
        
        t1.start();
    }
    
    
}
​

注:也可以通过继承的方式进行synchronized的重入

2.synchronized的异常

        对于web应用程序,异常释放锁的情况,如果不及时处理,很可能对你的应用程序业务逻辑产生严重的错误,比如你现在执行一个队列任务,很多对象都在去等待第一个对象正确执行完毕再去释放锁,但是第一个对象由于异常的出现,导致业务逻辑没有正常执行完毕。所以这一点一定要引起注意,在编写代码的时候,一定要考虑周全。

package com.ctb.sync5;
/**
 * synchronized的异常
 * 
 * @author biao
 *
 * 2024年
 */
public class SyncException {
​
    private int i = 0;
    public synchronized void operation(){
        while(true){
            try {
                i++;
                Thread.sleep(200);
                System.out.println(Thread.currentThread().getName() + " , i = " + i);
                if(i == 10){
                    Integer.parseInt("a");
                    //throw new RuntimeException();
                }
            } catch (Exception e) {//InterruptedException
                e.printStackTrace();
                System.out.println("log info i = " + i);
                //throw new RuntimeException();
                //continue;
            }
        }
    }
    
    public static void main(String[] args) {
        
        final SyncException se = new SyncException();
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                se.operation();
            }
        },"t1");
        t1.start();
    }
    
    
}

结果: 

注:在我们运行数据时,当它某条数据出现错误时,继续往下执行,并记录异常数据,后续根据日志去解决问题。

当然我们也可以在异常产生时进行停止数据的变动,

第一种:抛出运行时异常

public synchronized void operation(){
        while(true){
            try {
                i++;
                Thread.sleep(200);
                System.out.println(Thread.currentThread().getName() + " , i = " + i);
                if(i == 10){
                    Integer.parseInt("a");
//                  throw new RuntimeException();
                }
            } catch (Exception e) {//InterruptedException
                e.printStackTrace();
                System.out.println("log info i = " + i);
                throw new RuntimeException();
//              continue;
            }
        }
    }

结果: 

        第二种:InterruptedException 是 Java 中的一个受检查异常,表示一个线程被中断。当一个线程调用 Thread 类的 interrupt() 方法时,会给该线程设置一个中断标记,表示该线程被中断了。

public synchronized void operation(){
        while(true){
            try {
                i++;
                Thread.sleep(200);
                System.out.println(Thread.currentThread().getName() + " , i = " + i);
                if(i == 10){
                    Integer.parseInt("a");
//                  throw new RuntimeException();
                }
            } catch (InterruptedException e) {//InterruptedException
                e.printStackTrace();
                System.out.println("log info i = " + i);
//              throw new RuntimeException();
//              continue;
            }
        }
    }

结果: 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/712102.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

mini web框架示例

web框架: 使用web框架专门负责处理用户的动态资源请求,这个web框架其实就是一个为web服务器提供服务的应用程序 什么是路由? 路由就是请求的url到处理函数的映射,也就是说提前把请求的URL和处理函数关联好 管理路由可以使用一个…

浅谈网络通信(3)

文章目录 一、TCP[!]1.1、TCP协议报文格式1.2、TCP十大机制1.2.1、确认应答机制1.2.2、超时重传机制1.2.3、连接管理机制1.2.3.1、三次握手[其流程至关重要,面试必考]1.2.3.2.1、那为啥要建立连接??建立连接的意义是啥?&#xff1…

数据库管理-第204期 数据库的IO掉速,也许是SSD的锅(20240615)

数据库管理204期 2024-06-15 数据库管理-第204期 数据库的IO掉速,也许是SSD的锅(20240615)1 SSD物理结构2 SSD颗粒类型3 DRAM & SLC Cache3.1 DRAM3.2 SLC Cache3.3 其他方式 4 缓外降速总结 数据库管理-第204期 数据库的IO掉速&#xff…

System-Verilog 实现DE2-115流水灯

文章目录 一、 SystemVerilog1. SystemVerilog简介2. 基本语法和特性 二、实验过程hello.v文件引脚分配 三、实验效果参考 一、 SystemVerilog 1. SystemVerilog简介 SystemVerilog是一种高级的硬件描述语言(HDL),它不仅继承了Verilog语言的…

Qt项目天气预报(2) - 重写事件函数

鼠标右键实现退出界面 知识点QMenu: QMenu 弹出对话框 --> 相对QMessageBox 更加轻量点 QMenu是Qt库中用于创建弹出式菜单的类,它通常出现在应用程序的顶部菜单栏、按钮的右键菜单或自定义上下文菜单中。以下是关于QMenu的详细介绍: 1. 类的基本特…

JUnit 5学习笔记

JUnit 5 学习笔记 1.JUnit5的改变2.JUnit5常用注解及测试2.1 DisplayName/Disabled/BeforeEach/AfterEach/BeforeAll/AfterAll2.2 Timeout2.3 RepeatedTest 3.断言3.1 简单断言3.2 数组断言3.3 组合断言3.4 异常断言3.5 超时断言3.6 快速失败 4.前置条件5.嵌套测试6.参数化测试…

《Fundamentals of Power Electronics》——理想变压器基本公式推导

接下去推导理想变压器的基本公式。理想变压器满足以下三个条件: 1、无铜损。假设原副边线圈均无纯电阻,则不会因在铜导线中产生焦耳热引起能量损耗,另外也不考虑回路中的分布电容。 2、无铁损。忽略通过铁芯的磁通量变化引起的涡流损耗&…

DistilBertModel模型的简单解释

前言 DistilBertModel((embeddings): Embeddings((word\_embeddings): Embedding(30522, 768, padding\_idx0)(position\_embeddings): Embedding(512, 768)(LayerNorm): LayerNorm((768,), eps1e-12, elementwise\_affineTrue)(dropout): Dropout(p\0.1, inplaceFalse))(trans…

解决方案︱视频孪生智慧高速解决方案

系统概述 在交通强国战略的指导下,我国政府高度重视以数字化为核心的智慧高速公路建设与发展。2023年9月,交通运输部印发了《交通运输部关于推进公路数字化转型加快智慧公路建设发展的意见》,强调到2035年,全面实现公路数字化转型…

【多线程】Thread类及其基本用法

🥰🥰🥰来都来了,不妨点个关注叭! 👉博客主页:欢迎各位大佬!👈 文章目录 1. Java中多线程编程1.1 操作系统线程与Java线程1.2 简单使用多线程1.2.1 初步创建新线程代码1.2.2 理解每个…

IDEA创建lib目录,导入jar

IDEA创建lib目录,导入jar lib第一种创建方法: 当发现项目没有lib目录时,File>>>Project Structure 打开Artifacts目录 lib第二种创建方法: 按需选择需要的jar包或者全选即可 lib第三种创建方法:

MongoDB使用$addToSet向数组中添加元素

学习mongodb,体会mongodb的每一个使用细节,欢迎阅读威赞的文章。这是威赞发布的第66篇mongodb技术文章,欢迎浏览本专栏威赞发布的其他文章。如果您认为我的文章对您有帮助或者解决您的问题,欢迎在文章下面点个赞,或者关…

为什么白昼最长的地方不是在太阳的直射点

因为地球的自传轴相对于地球的公转平面成23.5度的夹角,地球自转一周是24小时,只有白天和黑夜两种状态。白天就可以定义为我们坐在地球上,跟着地球一直转,穿过有光照的地方所花的时间。 假设太阳光正对北回归线(这一天…

2024.ZCPC.M题 计算三角形个数

题目描述: 小蔡有一张三角形的格子纸,上面有一个大三角形。这个边长为 的大三角形, 被分成 个边长为 1 的小三角形(如图一所示)。现在,小蔡选择了一条水平边 删除(如图二所示),请你找出图上剩余…

把FydeOS塞进U盘,即插即用,小白都懂能看懂的Fyde To Go教程

前言 之前小白给各位小伙伴分享过关于Windows塞进U盘的教程: 把Windows系统装进U盘,从此到哪都有属于你自己的电脑系统 如果Windows都能装到U盘里,随身携带。那fydeOS能不能也像Windows一样塞到U盘里,实现FydeOS to Go呢&#…

树的基本概念

树(Tree) "树"这种数据很像现实生活中的“树”, 这里的每个元素我们叫做“节点” 用来连线相邻节点之间的关系,我们叫做“父子关系” A节点就是B节点的父节点,B节点是A节点的‘子节点’B,C,D这三个节点的…

Java基础语法Ⅰ【注释、关键字、字面量、变量】

Java基础语法① 注释关键字与标识符数据类型字面量和常量变量转义字符 注释 注释是在写代码时,对代码作出的一些解释说明,比如某一个函数的作用(功能)、函数接收的参数以及函数返回什么东西等等。 这些解释说明没有任何功能&…

C# Winform DPI自适应方案

Winform窗体随着屏幕的DPI缩放,会引起窗体变形及字体变形。 1.设置窗体和自定义用户控件的AutoScaleMode为None 实现目标:禁止窗体因为字体大小缩放变形 因为显示的高分屏,然后操作系统的设置了字体缩放引起的。窗体默认的AutoScaleMode = Font,控件会因为高分屏自动缩放…

遇到的状态308问题

前端用的vue.config.js做的代理,请求后端的地址https://n6118lr7-10010.usw3.devtunnels.ms 在请求的时候会308 是因为本地是http而请求地址是https 前端代理允许https接口代理即可

python pandas处理股票量化数据:笔记2

有一个同学用我的推荐链接注册了tushare社区帐号https://tushare.pro/register?reg671815,现在有了170分积分。目前使用数据的频率受限制。不过可以在调试期间通过python控制台获取数据,将数据保存在本地以后使用不用高频率访问tushare数据接口&#xf…