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的三种写法详解 的相关文章

随机推荐

  • 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 共享数据有修改的行为 具体不