Java 中的 synchronized 同步锁

        导致线程安全问题的根本原因在于,存在多个线程同时操作一个共享资源,要想解决这个问题,就需要保证对共享资源访问的独占性,因此人们在Java中提供了synchronized关键字,我们称之为同步锁,它可以保证在同一时刻,只允许一个线程执行某个方法或代码块。
        synchronized同步锁具有互斥性,这相当于线程由并行执行变成串行执行,保证了线程的安全性,但是损失了性能。下面我们先来看一下synchronized的使用方法。

synchronized 的使用方法

synchronized 的使用方法比较简单,修饰方式有如下两种。

  • 作用在方法级别,表示针对m1()方法加锁,当多个线程同时访问m1()方法时,同一时刻只有一个线程能执行。
public synchronized void m1(){//省略代码
}


作用在代码块级别,表示针对某一段线程不安全的代码加锁,只有访问到synchronized(this)这行代码时,才会去竞争锁资源。
 

public void m2( ){ synchronized(this){//省略代码}
}

        了解了 synchronized的基本使用语法之后,我们来看如图所示的流程,它针对上面的案例增加了 synchronized 同步锁之后的执行流程。简单地说,当多个线程同时访问加synchronized关键字修饰的方法时,需要先抢占一个锁标记,只有抢到锁标记的线程才有资格调用incr()方法。这就使得在同一时刻只有一个线程执行i++操作,从而解决了原子性问题。

了解 synchronized 同步锁的作用范围

        我们对一个方法增加synchronized关键字后,当多个线程访问该方法时,整个执行过程会变成串行执行,这种执行方式很明显会影响程序的性能,那么如何做好安全性及性能的平衡呢?
        实际上,synchronized关键字只需要保护可能存在线程安全问题的代码,因此,我们可以通过控制同步锁的作用范围来实现这个平衡机制。在synchronized 中,提供了两种锁,一是类锁,二是对象锁。
        类锁
        类锁是全局锁,当多个线程调用不同对象实例的同步方法时会产生互斥,具体实现方式如下。  

  • 修饰静态方法:
public static synchronized void m1( ){//省略代码
}
  • 修饰代码块,synchronized 中的锁对象是类,也就是Lock.class。
public class Lock{ public void m2(){synchronized(Lock.class){//省略代码}}
}


        下面这段程序使用类锁来实现跨对象实例,从而实现互斥的功能。

public class SynchronizedExample{public void m1( ) {synchronized(SynchronizedExample.class) {while (true){System.out.println("当前访问的线程:"+Thread.currentThread( ).getName());                         try{Thread.sleep(1000);} catch (InterruptedException e){e.printStackTrace();}}}}public static void main(String[] args){SynchronizedExample se1=new SynchronizedExample(); SynchronizedExample se2=new SynchronizedExample(); new Thread(()->se1.m1(),"t1").start(); new Thread(()->se2.m1(),"t2" ).start();}
}
  • 该程序中定义了一个m1()方法,该方法中实现了一个循环打印当前线程名称的逻辑,并且这段逻辑是用类锁来保护的。
  • 在 main()方法中定义了两个SynchronizedExample对象实例sel和se2,又分别定义了两个线程来调用这两个实例的m10方法。

        根据类锁的作用范围可以知道,即便是多个对象实例,也能够达到互斥的目的,因此最终输出的结果是:哪个线程抢到了锁,哪个线程就持续打印自己的线程名称。

        对象锁
        对象锁是实例锁,当多个线程调用同一个对象实例的同步方法时会产生互斥,具体实现方式如下。

  • 修饰普通方法:
public synchronized void m1( ){//省略代码
}
  • 修饰代码块,synchronized中的锁对象是普通对象实例。
public class Lock{Object lock=new 0bject( ); public void m2( ){synchronized(lock){//省略代码}}
}

        下面这段程序演示了对象锁的使用方法,代码如下。

public class SynchronizedForobjectExample {Object lock=new 0bject(); public void m1( ){synchronized (lock){while(true){System.out.println("当前获得锁的线程:"+Thread.currentThread().getName());try{Thread.sleep(1000);} catch (InterruptedException e){e.printStackTrace();}}}}public static void main(String[] args) {SynchronizedFor0bjectExample se1=new SynchronizedForobjectExample();             SynchronizedForObjectExample se2=new SynchronizedForobjectExample(); new Thread(()->se1.m1(),"t1").start(); new Thread(()->se2.m1(),"t2" ).start();}
}

我们先来看一下打印结果。

当前获得锁的线程:t1

当前获得锁的线程:t2

当前获得锁的线程:t2

当前获得锁的线程:t1

当前获得锁的线程:t1

当前获得锁的线程:t2

当前获得锁的线程:t1

当前获得锁的线程:t2


        从以上结果中我们发现,对于几乎相同的代码,在使用对象锁的情况下,当两个线程分别访问两个不同对象实例的m10方法时,并没有达到两者互斥的目的,看起来似乎锁没有生效,实际上并不是锁没有生效,问题的根源在于synchronized(lock)中锁对象lock的作用范围过小。
        Class是在JVM启动过程中加载的,每个.class文件被装载后会产生一个Class对象,Class对象在JVM进程中是全局唯一的。通过static修饰的成员对象及方法的生命周期都属于类级别,它们会随着类的定义被分配和装载到内存,随着类被卸载而回收。
        实例对象的生命周期伴随着实例对象的创建而开始,同时伴随着实例对象的回收而结束。

        因此,类锁和对象锁最大的区别是锁对象lock的生命周期不同,如果要达到多个线程互斥,那么多个线程必须要竞争同一个对象锁。
        在上述代码中,通过Objectlock-new Object();构建的锁对象的生命周期是由Synchronized- ForObjectExample 对象的实例来决定的,不同的SynchronizedForObjectExample 实例会有不同的 lock锁对象,由于没有形成竞争,所以不会实现互斥的效果。如果想要让上述程序达到同步的目的,那么我们可以对lock锁对象增加.static关键字。

static Object lock=new 0bject();

最后,留下一个问题去思考,关于 synchronized 同步锁的思考?同步锁的核心特性是排他,要达到这个目的,多线程必须抢占同一个资源。。。。。。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring Cloud之API网关(Gateway)

目录 API网关 好处 解决方案 Gateway 简介 特征 核心概念 Route(路由) Predicate(断言) Filter(过滤器) 工作流程 Route(路由) 路由配置方式 1.yml配置文件路由 2.bean进行配置 3.动态路由 动态路由 Predicate(断言) 特点 常见断言 示例 Filter(过滤器) …

IOC课程整理-19 Spring Environment 抽象

1. 理解 Spring Environment 抽象 2. Spring Environment 接口使用场景 3. Environment 占位符处理 4. 理解条件配置 Spring Profiles 5. Spring 4 重构 Profile 6. 依赖注入 Environment 7. 依赖查找 Environment 8. 依赖注入 Value 9. Spring 类型转换在 Environment 中的运用…

ZYNQ连载01-ZYNQ介绍

ZYNQ连载01-ZYNQ介绍 1. ZYNQ 参考文档:《ug585-zynq-7000-trm.pdf》 ZYNQ分为PS和PL两大部分,PS即ARM,PL即FPGA,PL作为PS的外设。 2. 方案 ZYNQ7020为双核A9架构,多核处理器常用的运行模式为AMP(非对称多处理)和…

测试C#调用Vlc.DotNet组件播放视频

除了Windows Media Player组件,在百度上搜索到还有不少文章介绍采用Vlc.DotNet组件播放视频,关于Vlc.DotNet的详细介绍见参考文献1,本文学习Vlc.DotNet的基本用法。   VS2022中新建基于.net core的winform程序,在Nuget包管理器中…

[论文精读]How Powerful are Graph Neural Networks?

论文原文:[1810.00826] How Powerful are Graph Neural Networks? (arxiv.org) 英文是纯手打的!论文原文的summarizing and paraphrasing。可能会出现难以避免的拼写错误和语法错误,若有发现欢迎评论指正!文章偏向于笔记&#x…

脚本木马编写

PHP小马编写 小马用waf扫描,没扫描出来有风险。 小马过waf之后用echo $_SERVER[DOCUMENT_ROOT]获得当前运行脚本所在的文档根目录。,然后在上传大马工具。 $_SERVER,参考:PHP $_SERVER详解 小马编写二次加密 现在是可以被安全…

辅助驾驶功能开发-功能规范篇(22)-4-L2级辅助驾驶方案功能规范

1.3.4 LDW系统功能定义 1.3.4.1 状态机 1.3.4.2 功能定义 1.3.4.2.1 信号需求列表 1.3.4.2.2 系统开启关闭 1)初始化 车辆上电后,车道偏离预警系统(LDW)进行初始化,控制器需要在上电后 220ms 内发出第一帧报文,并在 3s 内 完成内部自检,同时上电 3s 内不进行关联系统…

ProEssentials pro v9 历史更新列表--注册版

ProEssentials标准版和专业版之间的唯一区别是可以渲染的数据点和注释的数量。标准版与专业版一样拥有所有的功能和接口。所有版本包括WPF、WinForm、WebForm、ActiveX、VCL和DLL接口。标准版仅限于8000个数据点和800个图表注释。此限制适用于每个控件实例。你可以运行多个控件…

H5游戏源码分享-像素小鸟游戏(类似深海潜艇)

H5游戏源码分享-像素小鸟游戏&#xff08;类似深海潜艇&#xff09; 点击屏幕控制小鸟的飞行高度 整个小游戏就用JS完成 项目地址&#xff1a;https://download.csdn.net/download/Highning0007/88483228 <!DOCTYPE HTML> <html><head><meta http-equiv…

荣耀推送服务消息分类标准

前言 为了提升终端用户的推送体验、营造良好可持续的通知生态&#xff0c;荣耀推送服务将对推送消息进行分类管理。 消息分类 定义 荣耀推送服务将根据应用类型、消息内容和消息发送场景&#xff0c;将推送消息分成服务通讯和资讯营销两大类别。 服务通讯类&#xff0c;包…

进程替换..

1、单进程版 – 最简单的先看看程序替换 现象就是 1、我们用自己的进程封装了内置指令ls,并且代码中execl 后 printf 的after并没有打印出来。 2、谈进程替换的原理 单进程替换基本原理 上面例子中execl的做法非常简单粗暴&#xff0c;要调用ls&#xff0c;那么就把mycom…

整型在内存中的存储

前言&#xff1a; 本文章旨在从例题中加深对整型在数据中的存储的相关知识的理解。 首先我们需要明确整型在内存中都是以补码的形式进行计算 例1&#xff1a; 解析&#xff1a; 首先我们需要明确整型在内存中都是以补码的形式进行计算。 接着将一个整型类型的数据存储在ch…

详细介绍如何使用 NeRF 进行 3D 体积渲染-附源码下载

介绍 在此示例中,我们展示了 Ben Mildenhall 等人的研究论文 NeRF:将场景表示为用于视图合成的神经辐射场的最小实现 。等人。作者提出了一种巧妙的方法,通过神经网络对体积场景函数进行建模来合成场景的新颖视图。 为了帮助您直观地理解这一点,让我们从以下问题开始: 是…

基于FPGA的图像PSNR质量评估计算实现,包含testbench和MATLAB辅助验证程序

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 设置较大的干扰&#xff0c;PSNR15。 设置较小的干扰&#xff0c;PSNR25。 2.算法运行软件版本 matlab2022a vivado2019.2 3.部分核心程序 ti…

一周通过Professional Scrum Master(PSM1)考试准备分享

目录 一、为什么要考PSM 二、考试培训费用 三、学习时间 四、备考流程 1.通读Scrum Guide 2.完成Scrum Open的练习题3次 3.找题库刷题 4.再次完成Scrum Open的练习题3次 5.正式参加考试 五、其他考试准备 1.考试资格购买 2.语言 六、后记 一、为什么要考PSM 市面上有不少…

应用开发平台集成工作流系列之17——流程建模功能前端设计与改造回顾

背景 对于流程设置不友好的问题&#xff0c;国内钉钉另行设计与实现了一套流程建模模式&#xff0c;跟bpmn规范无关&#xff0c;有人仿照实现了下&#xff0c;并做了开源&#xff08;https://github.com/StavinLi/Workflow-Vue3&#xff09;&#xff0c;效果图如下&#xff1a…

蓝桥杯每日一题2023.10.28

题目描述 递增三元组 - 蓝桥云课 (lanqiao.cn) 题目分析 60分解法&#xff1a; 直接暴力循环每一个数进行比较 #include<bits/stdc.h> using namespace std; const int N 2e5 10; typedef long long ll; ll n, a[N], b[N], c[N], ans; int main() {cin >> n;…

从InnoDB索引的数据结构,去理解索引

从InnoDB索引的数据结构&#xff0c;去理解索引 1、InnoDB 中的 BTree1.1、BTree 的组成1.2、BTree中的数据页 2、聚簇索引2.1、聚簇索引的特点2.2、聚簇索引的结构示例2.3、聚簇索引的优缺点 3、非聚簇索引3.1、非聚簇索引结构示例3.2、关于回表3.3、聚簇索引和非聚簇索引的区…

STM32G030F6P6点灯闪烁

前言 &#xff08;1&#xff09;如果有嵌入式企业需要招聘湖南区域日常实习生&#xff0c;任何区域的暑假Linux驱动实习岗位&#xff0c;可C站直接私聊&#xff0c;或者邮件&#xff1a;zhangyixu02gmail.com&#xff0c;此消息至2025年1月1日前均有效 &#xff08;2&#xff0…

centos ubantu IP一直变化,远程连接不上问题

文章目录 一、为什么IP地址会变1.主机DHCP导致 二、解决IP地址变化1.centos2.ubantu 总结 虚拟机能连接为互联网,但下一次启动IP地址再发生变化,无法使用ssh远程连接 一、为什么IP地址会变 1.主机DHCP导致 虚拟机系统(ubantu,centos…)启动后会向本地申请IP地址租约,租聘的I…