java中synchronized的三种写法详解

2023-10-27

预备知识

首先,我们得知道在java中存在三种变量:

  1. 实例变量 ==》 存在于堆中
  2. 静态变量 ==》 存在于方法区中
  3. 局部变量 ==》 存在于栈中

然后,我们得明白,合适会发生高并发不安全

  • 条件1:多线程并发。
  • 条件2:有共享数据。
  • 条件3:共享数据有修改的行为。

具体不安全案例请参考 如下这篇文章:java线程安全问题详解_我想月薪过万的博客-CSDN博客icon-default.png?t=LA92https://blog.csdn.net/qq_41885673/article/details/121431714

在上面这篇文章银行取钱案例中,我们解决线程安全问题的方法是加了一个 synchronized 关键字。下面我们就详细介绍一下 synchronized 的三种写法,分别解决什么问题!!!

写法一:修饰代码块

package ThreadSafa;

public class Test {
    public static void main(String[] args) {
        TestAccount ta1 = new TestAccount();
        ta1.setNum(10);

        //共用一个账户对象
        TestThread t1 = new TestThread(ta1);
        TestThread t2 = new TestThread(ta1);
        t1.start();
        t2.start();
    }
}

class TestThread extends Thread {

    private TestAccount mAccount;

    public TestThread(TestAccount mAccount) {
        this.mAccount = mAccount;
    }

    @Override
    public void run() {
        mAccount.updateNum(1);
    }
}

class TestAccount {
    private double num;

    public double getNum() {
        return num;
    }

    public void setNum(double num) {
        this.num = num;
    }

    public void updateNum(int n) {
        synchronized (this) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            setNum(getNum() - n);
        }
        System.out.println(getNum());
    }
}

运行结果

 写法二:修饰方法

package ThreadSafa;

public class Test {
    public static void main(String[] args) {
        TestAccount ta1 = new TestAccount();
        ta1.setNum(10);

        TestThread t1 = new TestThread(ta1);
        TestThread t2 = new TestThread(ta1);
        t1.start();
        t2.start();
    }
}

class TestThread extends Thread {

    private TestAccount mAccount;

    public TestThread(TestAccount mAccount) {
        this.mAccount = mAccount;
    }

    @Override
    public void run() {
        mAccount.updateNum(1);
    }
}

class TestAccount {
    private double num;

    public double getNum() {
        return num;
    }

    public void setNum(double num) {
        this.num = num;
    }

    public synchronized void updateNum(int n) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        setNum(getNum() - n);
        System.out.println(getNum());
    }
}

运行结果

总结

可以看到 ,前面这两种写法其实是等价的,什么意思呢?就是当你用 synchronized 修饰共享对象 this 的时候 你就可以吧 synchronized 提到方法前面,但是我们一般不会这么干,因为扩大 synchronized 修饰的代码范围会使代码运行效率降低。

同时,前面两种方法都是为了解决 实例变量 线程安全问题而诞生的,对于静态变量我们怎么处理呢?请看写法三:

写法三:修饰静态方法

package ThreadSafa;

public class Test {
    public static void main(String[] args) {
        TestAccount ta1 = new TestAccount();
        TestAccount ta2 = new TestAccount();

        TestThread t1 = new TestThread(ta1);
        TestThread t2 = new TestThread(ta2);
        t1.start();
        t2.start();
    }
}

class TestThread extends Thread {

    private TestAccount mAccount;

    public TestThread(TestAccount mAccount) {
        this.mAccount = mAccount;
    }

    @Override
    public void run() {
        mAccount.updateCount(1);
    }
}

class TestAccount {
    private double num;
    public static double count = 10;

    public double getNum() {
        return num;
    }

    public void setNum(double num) {
        this.num = num;
    }

    public synchronized void updateNum(int n) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        setNum(getNum() - n);
        System.out.println(getNum());
    }

    public synchronized static void updateCount(int n) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        count -= n;
        System.out.println(count);
    }
}

运行结果展示

可以看到,在静态方法上加 synchronized 之后,他锁的是这个类,尽管两个账户对象不一样,但是,加了 synchronized 会保证他们排队执行,也就是保证了线程安全。

总结

局部变量 =》 存在于栈中 =》 线程之间不能共享 =》 所以数据永远是安全的

实例变量 =》 存在于堆中 =》 线程之间能共享 =》 采用写法一和写法二保证线程安全

静态变量 =》 存在于方法区 =》 线程之间能共享 =》 采用方写法三保证线程安全

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

java中synchronized的三种写法详解 的相关文章

  • Java - 因内存不足错误而关闭

    关于如何最好地处理这个问题 我听到了非常矛盾的事情 并且陷入了以下困境 OOME 会导致一个线程崩溃 但不会导致整个应用程序崩溃 我需要关闭整个应用程序 但不能 因为线程没有剩余内存 我一直认为最佳实践是让它们离开 这样 JVM 就会死掉
  • 如何强制jar使用(或jar运行的jvm)utf-8而不是系统的默认编码

    我的Windows默认编码是GBK 而我的Eclipse完全是utf 8编码 因此 在我的 Eclipse 中运行良好的应用程序崩溃了 因为导出为 jar 文件时这些单词变得不可读 我必须在 bat 文件中写入以下行才能运行该应用程序 st
  • 文本在指定长度后分割,但不要使用 grails 打断单词

    我有一个长字符串 需要将其解析为长度不超过 50 个字符的字符串数组 对我来说 棘手的部分是确保正则表达式找到 50 个字符之前的最后一个空格 以便在字符串之间进行彻底的分隔 因为我不希望单词被切断 public List
  • FileNotFoundException - Struts2 文件上传

    Strange FileNotFoundException使用Struts2上传文件时 这是 JSP 的一部分
  • 如何在java Spring Boot中实现通用服务类?

    我有许多具有重复代码的服务 我想知道如何实现通用服务 以便我的所有服务都可以扩展它 服务接口示例 重复代码 Service public interface IUserService List
  • 如何检测图像是否像素化

    之前有人在 SO 上提出过这样的问题 在Python中检测像素化图像 https stackoverflow com questions 12942365 detecting a pixelated image in python还有关于q
  • 如何使用正则表达式验证 1-99 范围?

    我需要验证一些用户输入 以确保输入的数字在 1 99 范围内 含 这些必须是整数 Integer 值 允许前面加 0 但可选 有效值 1 01 10 99 09 无效值 0 007 100 10 5 010 到目前为止 我已经制定了以下正则
  • org/codehaus/plexus/archiver/jar/JarArchiver(不支持的major.minor版本49.0)-Maven构建错误

    下午大家 我在尝试构建项目时收到上述错误 我很确定这与使用 Java 1 6 编译的 Maven 最新更新有关 而我们尝试构建的项目是 1 4 项目 在此之前的插件工作没有问题 因此我将以下内容添加到 POM xml 文件中以尝试强制使用现
  • Eclipse - 安装新的 JRE (Java SE 8 1.8.0)

    我正在尝试安装 Java 8 到目前为止我所做的 安装最新版本的 Eclipse 下载并安装 Java SE 运行时环境 8http www oracle com technetwork java javase downloads jre8
  • 如何删除日期对象的亚秒部分

    当 SQL 数据类型为时间戳时 java util Date 存储为 2010 09 03 15 33 22 246 如何在存储记录之前将亚秒设置为零 例如 在本例中为 246 最简单的方法是这样的 long time date getTi
  • 如何从日期中删除毫秒、秒、分钟和小时[重复]

    这个问题在这里已经有答案了 我遇到了一个问题 我想比较两个日期 然而 我只想比较年 月 日 这就是我能想到的 private Date trim Date date Calendar calendar Calendar getInstanc
  • Play.application() 的替代方案是什么

    我是 Play 框架的新手 我想读取conf文件夹中的一个文件 所以我用了Play application classloader getResources Data json nextElement getFile 但我知道 play P
  • Java - 从 XML 文件读取注释

    我必须从 XML 文件中提取注释 我找不到使用 JDOM 或其他东西来让它们使用的方法 目前我使用 Regex 和 FileReader 但我不认为这是正确的方法 您可以使用 JDOM 之类的东西从 XML 文件中获取注释吗 或者它仅限于元
  • 如何从 Ant 启动聚合 jetty-server JAR?

    背景 免责声明 I have veryJava 经验很少 我们之前在 Ant 构建期间使用了 Jetty 6 的包装版本来处理按需静态内容 JS CSS 图像 HTML 因此我们可以使用 PhantomJS 针对 HTTP 托管环境运行单元
  • 禁用 Android 菜单组

    我尝试使用以下代码禁用菜单组 但它不起作用 菜单项仍然启用 你能告诉我出了什么问题吗 资源 菜单 menu xml menu menu
  • Hadoop NoSuchMethodError apache.commons.cli

    我在用着hadoop 2 7 2我用 IntelliJ 做了一个 MapReduce 工作 在我的工作中 我正在使用apache commons cli 1 3 1我把库放在罐子里 当我在 Hadoop 集群上使用 MapReduceJob
  • 何时在 hibernate 中使用 DiscriminatorValue 注解

    在 hibernate 中使用 DiscriminatorValue 注释的最佳场景是什么以及何时 这两个链接最能帮助我理解继承概念 http docs oracle com javaee 6 tutorial doc bnbqn html
  • HttpClient请求设置属性问题

    我使用这个 HttpClient 库玩了一段时间 几周 我想以某种方式将属性设置为请求 不是参数而是属性 在我的 servlet 中 我想使用 Integer inte Integer request getAttribute obj 我不
  • try-with-resources 中出现死代码警告,但翻译后的 try-catch-finally 中没有出现死代码警告

    以下代码使用try 有资源 https docs oracle com javase specs jls se7 html jls 14 html jls 14 20 3Java 8 中引入的构造 偶尔抛出 方法被声明为抛出一个偶尔的异常
  • 即使调整大小,如何获得屏幕的精确中间位置

    好的 这个问题有两部分 当我做一个JFrame 并在其上画一些东西 即使我将宽度设置为 400 并使其在一个项目击中它时 当然 允许项目宽度 它会反弹回来 但由于某种原因 它总是偏离屏幕约 10 个像素 有没有办法解决这个问题 或者我只需要

随机推荐

  • Open3D 计算每个点的协方差矩阵

    目录 一 算法原理 1 计算公式 2 主要函数 3 函数源码 二 代码实现 三 结果展示 一 算法原理 1 计算公式 对于点云数据中的任意一点 p p p 根据其邻域内点的坐标计算其协方差矩阵 计算公式如下 M
  • kubesphere中间件部署

    微服务部署前中间件部署 一 MySQL部署 1 1 使用Docker实现MySQL主从复制 docker run p 3307 3306 name mysql master v mydata mysql master log var log
  • 文本处理(六)——Text-CNN、Word2Vec、RNN、NLP、Keras、fast.ai

    原文 https www jianshu com p 7f35a4b33f45 Text CNN Text CNN 文本分类 TextCNN 是利用卷积神经网络对文本进行分类的算法 由 Yoon Kim 在 Convolutional Ne
  • uiautomator2滑动操作d.swipe_ext(),及判断是否ui到顶部

    d swipe ext up 向上滑动 d swipe ext down 向下滑动 d swipe ext left 向左滑动 d swipe ext right 向右滑动 判断当前页面是否在底部 while True d swipe ex
  • java 反射和注解

    文章目录 1 反射 1 1定义反射 1 2java代码的三个阶段 1 3将类的各个部分封装的对象 1 4获取 1 4 1获取类名 1 4 2获取成员变量 1 4 3获取构造方法 1 4 4获取成员方法 1 4 5获取注解 2 注解 2 1概
  • 调试之设置数据断点 (zz.IS2120)

    How to Set a Data Breakpoint Native Only z 2012 09 11 09 43 48 IS2120 CSDN T3678804781 T31 L404 R7 V204 Data breakpoints
  • 【计算机网络】Linux环境中的网络套接字编程

    文章目录 前言 一 预备知识 理解源IP地址和目的IP地址 认识端口号 认识UDP协议和TCP协议 了解网络字节序 二 socket 套接字 socket 常见API sockaddr 和 sockaddr in 三 UDP Socket编
  • bash脚本-centos7安装docker

    bin bash set ex sudo yum remove docker docker client docker client latest docker common docker latest docker latest logr
  • 蓝桥杯:试题 算法训练 星际交流 康托展开

    题目 资源限制 时间限制 1 0s 内存限制 256 0MB 问题描述 人类终于登上了火星的土地并且见到了神秘的火星人 人类和火星人都无法理解对方的语言 但是我们的科学家发明了一种用数字交流的方法 这种交流方法是这样 的 首先 火星人把一个
  • MATLAB计算矩阵的逆和广义逆

    当矩阵的行数等于列数时 计算矩阵的逆 可直接使用 inv A 当矩阵的行数不等于列数时 可以考虑计算矩阵的Moore Penrose逆 有两种方法 第一 直接使用Moore Penrose逆的而定义B inv A A A 第二 使用命令B
  • RedisTemplate redis缓存的基本使用

    文章目录 RedisTemplate redis缓存的基本使用 放入缓存 并缓存设置过期时间 时间单位为秒如果值小于或等于0为过期时间无限期 放入缓存 过期时间无限制 如果key存在不改变其值 不存在此key就放入缓存 并且key存在返回f
  • error: Your local changes to the following files would be overwritten by merge: .DS_Store

    Git ignore gitignore 有时候会遇到如下提示 error Your local changes to the following files would be overwritten by merge DS Store b
  • 高德API+Echarts 实现3D地图展示图表

    效果图 前期准备 所需依赖 echarts amap amap jsapi loader npm i echarts amap amap jsapi loader 代码实现 3D地图 div div 您在2021年12月02日以后申请的ke
  • 【OpenWRT之旅】如何自定义一个配置文件的设置界面

    1 引言 OpenWRT中采用LuCI作为它的Web interface界面框架 采用Lua语言 在本文中将以一个简单的示例详细描述如何自定义开发一个界面 对一个配置文件进行操作 2 Model与Controler MVC的设计理念是进行L
  • JS中的键盘事件(onkeydown、onkeyup、keyCode)

    键盘事件 okeydown 键盘被按下 如果一直按着键盘的按键 则okeydown事件会一直被触发 当键盘按键一直被按住的时候 事件被连续触发 第一次和第二次以及后面的n次之间 触发的时间间隔会稍长 在0 5秒左右 一直按着按键 事件连续触
  • 麒麟系统里如何通过命令查询当前系统的具体信息

    命令 cat etc kyinfo 获取系统详细信息 包含架构 包含当前系统更新的时间等 cat etc productinfo 部分环境可能不能通过此命令获取信息 R系 nkvers 其他查询命令 操作系统版本查询命令 cat etc k
  • 前端性能优化认知

    前端性能优化认知 什么是前端性能优化 通常来讲 前端性能优化是指 从用户开始访问网站到整个页面完整地展现出来的过程中 通过各种优化策略和优化方法 让页面加载得更快 让用户的操作相应更及时 给用户更好的使用体验 优化是在做什么 如上图所示 优
  • ESP8266-01S烧录MQTT透传AT固件

    一 ESP8266 01S模块硬件连接 须在正常模式下 VCC GND RX TX 将IO0引脚接地 EN引脚接3 3V 总共6根线 连入USB TO TTL模块 将USB TO TTL模块插入电脑 二 固件与工具均可在安信可官网下载 也可
  • Element ui 中将switch开关自定义文字描述(ON/OFF)显示在开关里面

    Element ui 中将switch开关自定义文字描述 ON OFF 显示在开关里面 官网示例 可以看出文字描述在开关两边 看着很别扭 上代码
  • java中synchronized的三种写法详解

    预备知识 首先 我们得知道在java中存在三种变量 实例变量 存在于堆中 静态变量 存在于方法区中 局部变量 存在于栈中 然后 我们得明白 合适会发生高并发不安全 条件1 多线程并发 条件2 有共享数据 条件3 共享数据有修改的行为 具体不