千家信息网

java中synchronized关键字的3种写法分别是什么

发表于:2025-01-20 作者:千家信息网编辑
千家信息网最后更新 2025年01月20日,java中synchronized关键字的3种写法分别是什么,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。预备知识首先,我们得知道在
千家信息网最后更新 2025年01月20日java中synchronized关键字的3种写法分别是什么

java中synchronized关键字的3种写法分别是什么,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

    预备知识

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

    • 实例变量 ==》 存在于堆中

    • 静态变量 ==》 存在于方法区中

    • 局部变量 ==》 存在于栈中

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

    • 条件1:多线程并发。

    • 条件2:有共享数据。

    • 条件3:共享数据有修改的行为。

    具体不安全案例请参考 如下这篇文章:java线程安全问题详解

    在上面这篇文章银行取钱案例中,我们解决线程安全问题的方法是加了一个 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 会保证他们排队执行,也就是保证了线程安全。

    synchronized原理

    Synchronized是通过对象内部的一个叫做监视器锁(monitor)来实现的。但是监视器锁本质又是依赖于底层的操作系统的互斥锁(Mutex Lock)来实现的。而操作系统实现线程之间的切换这就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因。这种依赖于操作系统互斥锁(Mutex Lock)所实现的锁我们称之为"重量级锁"。

    1. monitor锁定过程

    当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:

    a、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。

    b、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.

    c、如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

    2. synchronized锁

    Java SE1.6对Synchronized进行了各种优化之后,它并不那么重了。在不同的场景中引入不同的锁优化。

    1.偏向锁:适用于锁没有竞争的情况,假设共享变量只有一个线程访问。如果有其他线程竞争锁,锁则会膨胀成为轻量级锁。

    2.轻量级锁:适用于锁有多个线程竞争,但是在一个同步方法块周期中锁不存在竞争,如果在同步周期内有其他线程竞争锁,锁会膨胀为重量级锁。

    3.重量级锁:竞争激烈的情况下使用重量级锁。

    偏向锁和轻量级锁之所以会在性能上比重量级锁是因为好,本质上是因为偏向锁和轻量级锁仅仅使用了CAS。

    3. synchronized锁优化

    尽量采用轻量级锁和偏向锁等对Synchronized的优化,但是这两种锁也不是完全没缺点的,比如竞争比较激烈的时候,不但无法提升效率,反而会降低效率,因为多了一个锁升级的过程,这个时候就需要通过-XX:-UseBiasedLocking来禁用偏向锁。

    总结

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

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

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

    关于java中synchronized关键字的3种写法分别是什么问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注行业资讯频道了解更多相关知识。

    线程 写法 变量 方法 安全 问题 竞争 之间 轻量 轻量级 静态 对象 效率 保证 运行 关键 关键字 操作系统 代码 实例 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 网络安全知识英文术语 软件开发项目经理胜任能力 大恒文档安全服务器端 当网络技术30岁 华中科技大学网络安全学院的复试 数据模型对应数据库设计那个步骤 游享星域网络技术 软件开发项目基本开发过程 网络安全研讨会 中央网络安全中心 网络安全分为两个信息网络 浦东新区软件开发企业招聘 以下哪个不属于数据库设计 龙口微信公众号软件开发公司 香港服务器的工作原理 网络安全工程师 要学什么 扬州服务器工控机的配置 上海能耗管理软件开发多少钱 湖南软件开发的大学 网络安全产业创新篇章 网络安全问题的研究个人总结 天天乐棋牌软件开发是否安全 怎样用网络安全技术验证微信号 r中读入其他格式数据库 应用程序和服务器区别 新疆升鑫圣网络技术有限公司 断言是软件开发中一种常见的 网络安全宣传周 甘肃 电影数据库小程序 数据库查询表结构
    0