当前位置: 首页 > news >正文

java并发:synchronized锁详解

背景:

        在java多线程当中,我们总有遇到过多个线程操作一个共享数据时,而这个最后的代码执行结果并没有按照我们的预期一样得到正确的结果。此时我们就需要让代码执行在操作共享变量时,要等一个线程操作完毕时,另一个线程才能去操作这个共享变量。synchronized锁就能达到这样的目的。在线程A操作某个共享变量时,其他线程想要操作这个对像的话只能先处于等待状态,只有线程A操作完毕后其他线程才能操作这个变量。synchronized还有一个作用,就是将让其他线程看到这个线程内对像的变化,获取到对像的最新的值。

sychronized锁的使用方式

        利用synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现 为以下3种形式。

  • 对于普通同步方法,锁是当前实例对象。
  • 对于静态同步方法,锁是当前类的Class对象。
  • 对于同步方法块,锁是Synchonized括号里配置的对象。

下面我对于这三种方式进行详细的说明,但再此之前要介绍多线程操作共享变量的内存图:

 多线程处理共享数据是从主内存中将数据拷贝一份到自己的工作内存当中,再工作内存当中对其进行操作,然后再写回到主内存当中。

对于普通同步方法,锁是当前实例对象。

        当sychronized修饰普通方法时,这个方法已经变为了同步方法,并对这个对象加了锁,此时想要操作这个对像的同步方法要先获得这个对像的锁,所以说这个加锁并不是其他线程都无法访问这个对象了,而是无法访问这个对像内被sychronized修饰变为同步方法的方法了,其他方法并不受影响。

public class ThreadTest {public synchronized  void m1() {    //同步方法System.out.println("m1方法开始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m1方法结束");}public synchronized void m2() {   //同步方法System.out.println("m2方法开始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m2方法结束");}public static void main(String[] args) {ThreadTest threadTest = new ThreadTest();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {threadTest.m1();}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {threadTest.m2();}});t1.start();t2.start();}
}

此时m1和m2方法都是同步方法,我们创建一个ThreadTest对像,然后用两个线程分别调用同一个对像的m1和m2方法,根据前面两个所讲,可以预测由于线程1执行方法m1时就拿到了这个对像锁,则其他线程无法执行这个对像的其他同步方法。执行结果不出我们所料:

确实是线程1先执行m1,等其执行完毕后线程2再执行的m2方法。当我们将m2变为普通方法(不被关键字synchronized修饰),然后执行:

这时候就没有等待线程1执行同步方法m1完毕后线程2再调用m2方法。因为此时执行普通方法m2不需要这个对像的锁,也就不用等待线程A执行完释放锁了。

对于静态同步方法,锁是当前类的Class对象。 

        当synchronized修饰的是静态方法时,此时加锁的并不是这个类的实例对象了,而是这个类的Class对象。此时和前面那种形式有一些不同了。但一个线程调用这个静态同步方法时会获取到这个Class对像的锁,其他线程就不能再执行这个类的其他静态同步方法。但可以调用其他普通的静态方法和普通方法。同时也可以调用它的普通同步方法,因为调用这两者方法需要的锁并不一样,一个是类锁,一个是对像锁,所以他们并不互斥。下面代码结果展示一下:

public class ThreadTest {public synchronized  void m1() {System.out.println("m1方法开始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m1方法结束");}public synchronized static void m2() {System.out.println("m2方法开始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m2方法结束");}public synchronized static void m3() {System.out.println("m3方法开始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m3方法结束");}public static void main(String[] args) {ThreadTest threadTest = new ThreadTest();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {threadTest.m1();}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {ThreadTest.m2();}});Thread t3 = new Thread(new Runnable() {@Overridepublic void run() {ThreadTest.m3();}});t1.start();t2.start();t3.start();}
}

我定义了三个方法都用synchronized修饰了,m1是普通同步方法,而m2、m3是静态同步方法,并用三个线程分别调用这三个方法。按照前面原理解释,m1方法和m2、m3这两个方法不互斥,m2和m3这两个方法互斥。

由结果看出,m1和m2方法可以看出不互斥,同时执行,并没有等待,而m3方法是再m2方法完全执行完毕后再执行的。可以说明m2执行时获取了Class对象的锁,而静态同步方法m3想要执行的话,就得等待锁的释放才能执行,最终产生了上面的执行结果。 

对于同步方法块,锁是Synchonized括号里配置的对象。

        锁住同步方法块,锁是synchonized括号里面那个引用类型对像(注意:能作为锁的只能是引用类型,不能是基本数据类型)。这样子实现的功能是:因为线程的切换是随机的,但我们要保证一段代码一定要全部执行,不想被执行到一办事就被切换到其他线程,无法保证线程安全。此时就需要synchonized来锁住这段代码了。还有就是想要执行这个代码块就得先获取到相对应的对象锁。也就是说当两个线程执行两个拿同一个对象作为锁的代码块,则两者不能同时执行,必须等一个代码块执行完毕,释放锁后,另一个线程才能获取这个对像锁然后执行。

public class ThreadTest {public static Object o = new Object();public   void m1() {synchronized(o) {System.out.println("m1方法开始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m1方法结束");}}public   void m2() {synchronized(o){System.out.println("m2方法开始");try {Thread.sleep(1000);} catch (Exception e) {}System.out.println("m2方法结束");}}public static void main(String[] args) {ThreadTest threadTest = new ThreadTest();Thread t1 = new Thread(new Runnable() {@Overridepublic void run() {threadTest.m1();}});Thread t2 = new Thread(new Runnable() {@Overridepublic void run() {threadTest.m2();}});t1.start();t2.start();}
}

代码中两个线程各执行一个代码块,两个代码块用的是同一个对象锁,则他们不能同时执行是互斥的。

由输出结果可以看出,线程2中的代码块是等线程1执行完其内的代码块并释放锁后再执行的。 

相关文章:

java并发:synchronized锁详解

背景: 在java多线程当中,我们总有遇到过多个线程操作一个共享数据时,而这个最后的代码执行结果并没有按照我们的预期一样得到正确的结果。此时我们就需要让代码执行在操作共享变量时,要等一个线程操作完毕时,另一个线程…...

Unity 之NavMeshAgent 组件(导航和路径寻找的组件)

文章目录 **作用**:**属性和方法**:**用途**:**注意事项**: NavMeshAgent 是Unity引擎中用于导航和路径寻找的组件。它可以使游戏对象在场景中自动找到可行走的路径,并在避免障碍物的情况下移动到目标位置。 以下是关于…...

装箱和拆箱

1. 概念 装箱 将值类型转换成等价的引用类型 装箱的步骤 拆箱 将一个已装箱的引用类型转换为值类型,拆箱操作需要声明拆箱后转换的类型 拆箱的步骤 1)获取已装箱的对象的地址 2)将值从堆上的对象中复制到堆栈上的值变量中 2. 总结 装箱和拆箱…...

等保测评--安全通信网络--测评方法

安全子类--安全架构 a)应保证网络设备的业务处理能力满足业务高峰期需要; 一、测评对象 路由器、交换机、无线接入设备和防火墙等提供网络通信功能的设备或相关组件 二、测评实施 1) 应核查业务高峰时期一段时间内主要网络设备(一般包括核心交换机、汇聚交换机、边界路…...

统计学补充概念11-tsne

概念 t-SNE(t-distributed Stochastic Neighbor Embedding)是一种非线性降维技术,用于可视化高维数据在低维空间中的分布。与主成分分析(PCA)等线性降维方法不同,t-SNE专注于保留数据点之间的局部相似性关…...

Linux_11_系统启动和内核管理

目录 1 C entOS 6 的启动管理1.1 Linux 组成1.2 内核设计流派1.3 CentOS 6启动流程1.3.1 CentOs 6 启动流程1.3.1 硬件启动POST1.3.2 bootloader 启动/引导加载器1.3.2.1 grub 功能和组成1.3.2.2 CentOS 6 grub 安装1.3.2.3 grub legacy 管理 1.3.3 加载 kernel1.3.4 init 初始…...

【从零学习python 】65. Python正则表达式修饰符及其应用详解

文章目录 正则表达式修饰符进阶案例 正则表达式修饰符 修饰符描述re.I使匹配对大小写不敏感re.M多行匹配,影响 ^ 和 $re.S使 . 匹配包括换行在内的所有字符 示例代码如下: import reprint(re.search(rL, hello)) # None# 不区分大小写,可…...

QA2

1. import shutil 是什么意思? 在 Python 中,import shutil 是导入标准库 shutil 的语句。shutil 提供了一些用于复制文件和文件夹、移动文件和文件夹、以及执行其他文件操作的函数。 通过导入 shutil,你可以使用其中的函数来处理文件和文件…...

centos7卸载docker

要在CentOS 7上干净地卸载Docker,可以执行以下步骤: 停止Docker服务: sudo systemctl stop docker移除所有Docker容器和镜像。这将删除所有相关数据,包括容器、镜像以及存储卷等。请注意,这将不可逆转地删除数据。 …...

【计算机视觉】递归神经网络在图像超分的应用Deep Recursive Residual Network for Image Super Resolution

DRCN: Deeply-Recursive Convolutional Network for Image Super-Resolution 总结 这篇文章是第一次将之前已有的递归神经网络(Recursive Neural Network)结构应用在图像超分辨率上。为了增加网络的感受野,提高网络性能,引入了深度递归神经网络&#x…...

Centos 7 安装系列(8):openGauss 3.0.0

安装依赖包: yum -y install libaio-devel flex bison ncurses-devel glibc-devel patch redhat-lsb-core readline-devel openssl-devel sqlite-devel libnsl 安装插件: yum install -y bzip2 net-tools为什么要安装这两个? 安装bzip2 是…...

NOIP真题讲解 传球游戏 接水问题

传球游戏 说明 上体育课的时候,小蛮的老师经常带着同学们一起做游戏。这次,老师带着同学们一起做传球游戏。 游戏规则是这样的:n个同学站成一个圆圈,其中的一个同学手里拿着一个球,当老师吹哨子时开始传球,…...

《论文阅读18》 SSD: Single Shot MultiBox Detector

一、论文 研究领域: 2D目标检测论文:SSD: Single Shot MultiBox Detector ECCV 2016 数据集 论文链接论文github 二、论文概要 SSD网络是作者Wei Liu在ECCV 2016上发表的论文。对于输入尺寸300x300的网络 使用Nvidia Titan X在VOC 2007测试集上达到74…...

NOIP2016普及组第四题 魔法阵

魔法阵 题目描述 六十年一次的魔法战争就要开始了,大魔法师准备从附近的魔法场中汲取魔法能量。 大魔法师有m个魔法物品,编号分别为1,2,…,m。每个物品具有一个魔法值,我们用Xi表示编号为i的物品的魔法值。每个魔法值Xi是不超过n的正整数&…...

uniapp-滑块验证组件wo-slider

wo-slider是一款支持高度自定义的滑块验证组件,采用uniapp-vue2编写 采用touchstart、touchmove、touchend事件实现的滑块组件,支持H5、微信小程序(其他小程序未试过,可自行尝试) 可到插件市场下载尝试: https://ext.…...

NPM 管理组织成员

目录 1、向组织添加成员 1.1 邀请成员加入您的组织 1.2 撤销组织邀请 2、接收或拒接组织邀请 2.1 接收组织邀请 2.2 拒绝组织邀请 3、组织角色和权限 4、管理组织权限 5、从组织中删除成员 1、向组织添加成员 作为组织所有者,您可以将其他npm用户添加到…...

设计模式(3)抽象工厂模式

一、概述: 1、提供一个创建一系列相关或相互依赖对象的接口,而无须指定它们具体的类。 2、结构图: 3、举例代码: (1) 实体: public interface IUser {public void insert(User user);public…...

【C++】早绑定、析构与多态 | 一道关于多态的选择题记录

今天在和群友聊天的时候看到了一道很坑的题目&#xff0c;分享给大家 1.看题&#xff01; 先来看看题目 struct Dad { public:Dad(){ echo();}~Dad(){ echo();}virtual void echo() {cout << "DAD ";} };struct Son:Dad { public:void echo() const override…...

mac下安装tomcat

1. 官网下载Apache Tomcat - Apache Tomcat 9 Software Downloads 2. 授权bin目录下所有.sh文件权限sudo chmod 755 *.sh 3. 启动程序(后台运行) sudo sh ./startup.sh 4. 在当前窗口启动程序&#xff0c;随时看到日志sudo sh ./catalina.sh run 5. 关闭程序 sudo sh ./shu…...

【小梦C嘎嘎——启航篇】string常用接口的模拟实现

【小梦C嘎嘎——启航篇】string常用接口的模拟实现&#x1f60e; 前言&#x1f64c;string 模拟实现1、iterator 迭代器相关使用函数实现2、构造函数接口实现3、 传统写法——拷贝构造函数接口实现4、 现代写法——拷贝构造函数接口实现5、析构函数接口实现6、传统写法—— 赋…...

uniapp 对接腾讯云IM群组成员管理(增删改查)

UniApp 实战&#xff1a;腾讯云IM群组成员管理&#xff08;增删改查&#xff09; 一、前言 在社交类App开发中&#xff0c;群组成员管理是核心功能之一。本文将基于UniApp框架&#xff0c;结合腾讯云IM SDK&#xff0c;详细讲解如何实现群组成员的增删改查全流程。 权限校验…...

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

调用支付宝接口响应40004 SYSTEM_ERROR问题排查

在对接支付宝API的时候&#xff0c;遇到了一些问题&#xff0c;记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

全球首个30米分辨率湿地数据集(2000—2022)

数据简介 今天我们分享的数据是全球30米分辨率湿地数据集&#xff0c;包含8种湿地亚类&#xff0c;该数据以0.5X0.5的瓦片存储&#xff0c;我们整理了所有属于中国的瓦片名称与其对应省份&#xff0c;方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...

Mac下Android Studio扫描根目录卡死问题记录

环境信息 操作系统: macOS 15.5 (Apple M2芯片)Android Studio版本: Meerkat Feature Drop | 2024.3.2 Patch 1 (Build #AI-243.26053.27.2432.13536105, 2025年5月22日构建) 问题现象 在项目开发过程中&#xff0c;提示一个依赖外部头文件的cpp源文件需要同步&#xff0c;点…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

R语言速释制剂QBD解决方案之三

本文是《Quality by Design for ANDAs: An Example for Immediate-Release Dosage Forms》第一个处方的R语言解决方案。 第一个处方研究评估原料药粒径分布、MCC/Lactose比例、崩解剂用量对制剂CQAs的影响。 第二处方研究用于理解颗粒外加硬脂酸镁和滑石粉对片剂质量和可生产…...