Java并发——synchronized关键字

news2025/3/5 6:28:51

Java并发——synchronized关键字

1.synchronized作用的范围

synchronized有两种作用范围:对象锁类锁

对象锁

使用方式: 在普通方法上加synchronized(默认锁对象为this)和同步代码块(自己指定锁对象)

在普通方法上加锁(默认锁对象为this)

public class SynchronizedLock implements Runnable{

    static  SynchronizedLock instance = new SynchronizedLock();

    @Override
    public void run() {
        method();
    }

    // 修饰普通方法 默认锁this
      public synchronized void method (){
            System.out.println("线程"+Thread.currentThread().getName()+"开始执行");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程"+Thread.currentThread().getName()+"结束");
        }



    public static void main(String[] args) {

        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);

        t1.start();
        t2.start();
    }
}

/* 执行结果
 *
 *线程Thread-0开始执行
 *线程Thread-0结束
 *线程Thread-1开始执行
 *线程Thread-1结束
 *
 */

同步代码块指定锁对象this

public class SynchronizedLock implements Runnable{

    static  SynchronizedLock instance = new SynchronizedLock();

    @Override
    public void run() {
        //同步代码块——锁this,两个线程使用的锁都是instance对象的锁,线程1需要等线程0释放锁

        synchronized (this){
            System.out.println("线程"+Thread.currentThread().getName()+"开始执行");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程"+Thread.currentThread().getName()+"结束");
        }
    }


    public static void main(String[] args) {

        Thread t1 = new Thread(instance);
        Thread t2 = new Thread(instance);

        t1.start();
        t2.start();
    }
}

/* 执行结果
 *
 *线程Thread-0开始执行
 *线程Thread-0结束
 *线程Thread-1开始执行
 *线程Thread-1结束
 *
 */

类锁

synchronized修饰静态方法或者指定锁对象为Class对象

修饰静态方法

public class SynchronizedLock implements Runnable{
  // 使用两个不同的对象 证明结果是由类锁造成的
    static  SynchronizedLock instance1 = new SynchronizedLock();
    static  SynchronizedLock instance2 = new SynchronizedLock();
    @Override
    public void run() {
        method();
    }
  // synchronized修饰静态方法,默认锁的就是当前class类
      public static synchronized void method (){
            System.out.println("线程"+Thread.currentThread().getName()+"开始执行");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程"+Thread.currentThread().getName()+"结束");
        }



    public static void main(String[] args) {

        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance2);

        t1.start();
        t2.start();
    }
}

/* 执行结果
 *
 *线程Thread-0开始执行
 *线程Thread-0结束
 *线程Thread-1开始执行
 *线程Thread-1结束
 *
 */

注意: 结果虽然是和锁普通方法相同,但是锁静态方法使用的是两个不同的对象instance1 instance2,造成两个线程串行执行是因为sychronized锁的是当前类,两个对象使用一把锁

指定锁对象为Class类

public class SynchronizedLock implements Runnable{
  // 使用两个不同的对象 证明结果是由类锁造成的
    static  SynchronizedLock instance1 = new SynchronizedLock();
    static  SynchronizedLock instance2 = new SynchronizedLock();
    @Override
    public void run() {
        // synchronized修饰指定类,锁对应的Class类
        synchronized (SynchronizedLock.class){
            System.out.println("线程"+Thread.currentThread().getName()+"开始执行");

            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("线程"+Thread.currentThread().getName()+"结束");
        }
    }



    public static void main(String[] args) {

        Thread t1 = new Thread(instance1);
        Thread t2 = new Thread(instance2);

        t1.start();
        t2.start();
    }
}

/* 执行结果
 *
 *线程Thread-0开始执行
 *线程Thread-0结束
 *线程Thread-1开始执行
 *线程Thread-1结束
 *
 */

2.synchronized原理

synchronized基于JVM实现,随着JVM的升级不断优化。

在JVM虚拟机中,对像在内存中的存储分为三个部分:

  • 对象头
  • 实例数据
  • 对齐填充

在这里插入图片描述

标记字段Mark Word

用于存储对象自身的运行时数据,HashCodeGC Age锁标记位是否为偏向锁

64位JVM中Mark Word组成
在这里插入图片描述

加锁

每一个对象在同一时间只与一个Monitor监视器相关联,而一个Monitor在同一时间只能被一个线程获得,一个对象在尝试获取与这个对象相关联的Monitor锁的所有权时,Monitorenter指令会发生三种情况

  • Monitor计数器为0,表示目前没有其他线程获取,该线程会立刻获取并把锁计数器加1,之后其他线程想要获取这个monitor就需要等待
  • 如果这个Monitor已经获取了这个锁的所有权,又重入了这把锁,锁计数器进行累加
  • 这把锁已经被其他线程获取了,等待锁释放

释放锁

释放对于monitor的所有权,将monitor的计数器减1,如果减完之后计数器不为0,代表是重入的当前线程还继续持有这把锁,如果计数器为0,表示当前线程不再拥有monitor的所有权,就是释放锁

在这里插入图片描述

总结:

任意一个线程对某个对象的访问,首先要获取对象的监视器,如果获取失败,线程就进入同步队列中等待,当对象的监视器被其他线程释放后,在同步队列中的线程就重新尝试获取该监视器

可重入原理:

当一个线程获取对象锁成功后,该对象的monitor锁计数器+1,当该线程再次获取时计数器再+1,释放时释放一次就计数器-1,直到减为0,锁被释放(计数器次数就是重入次数)

3.锁升级

无锁 --> 偏向锁 --> 轻量级锁 --> 重量级锁

  • 无锁

没有对资源进行锁定,所有线程都能访问修改同一个资源,但只有一个线程能修改成功

  • 偏向锁

大多数情况下,锁不仅不存在多线程竞争,而且总是由同一个线程多次获得,为了让线程获取锁的代价更低而引入了偏向锁。当一个线程访问同步代码块并获取锁时,会在对象头和栈帧中记录锁偏向的线程ID。在线程访问同步代码块时先判断对象头的Mark Word里面是否存储指向当前线程的偏向锁,如果存在就直接获取锁。

  • 轻量级锁

当多个线程尝试竞争偏向锁时,锁升级为轻量级锁。线程在执行同步块之前,JVM会先在当前线程的栈帧中创建用于锁记录的空间(Lock Record),并采用CAS将对象头中的Mark Word替换成指向锁记录的指针。如果成功,当前线程获取锁,如果失败,标识其他线程竞争锁,当前线程尝试自旋来获取锁

  • 重量级锁

线程通过自旋获取锁,在自旋的过程中占用消耗CPU资源,如果长时间自旋会影响性能。因此需要设定自旋次数,默认10次,如果10次自旋依旧没获得锁,则锁升级为重量级锁。当其他线程尝试获取锁时,都会被阻塞,当持有锁的线程释放锁后,才会唤醒这些线程

4.synchronized的优缺点

synchronized的优点

  • 使用简单,不需要手动去释放锁
  • 基于JVM实现,在迭代中不断优化升级

synchronized的缺点

  • 效率低:锁的释放情况少,只有代码执行完毕或者异常结束才会释放锁,尝试获取锁时不能设置超时,也不能中断一个正在使用锁的线程
  • 不够灵活:加锁和释放锁的时机单一,没有Lock灵活

使用synchronized的注意点

  • 锁对象不能为空,因为锁的信息都保存在对象头中
  • 作用范围不宜过大,影响程序执行的效率
  • 避免死锁

参考文章

《Java并发编程的艺术》

https://pdai.tech/md/java/thread/java-thread-x-key-synchronized.html

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022全年度净水器十大热门品牌销量榜单

随着人们健康意识的提升,每天喝足量水的观念已经深入人心,而伴随居民生活水平的提高,当下居民对水污染问题也更加关注,对饮水品质的认知和要求也随之升级。因此,净水器在过去几年开启了高速增长的趋势。 根据鲸参谋数据…

【AI with ML】第 14 章 :在 iOS 应用程序中使用 TensorFlow Lite

🔎大家好,我是Sonhhxg_柒,希望你看完之后,能对你有所帮助,不足请指正!共同学习交流🔎 📝个人主页-Sonhhxg_柒的博客_CSDN博客 📃 🎁欢迎各位→点赞…

使用文档管理系统结束内容混乱

使用文档管理系统结束内容混乱 在我们目前所处的混合工作新环境中,数据和文档量快速增长而导致的“内容混乱”是效率和有效协作的常见障碍。管理这个问题可能是现代企业面临的最大挑战。 根据分析公司IDC发布的预测,2020年至2024年间创建的数据量将超过…

工控CTF之协议分析5——COTP

协议分析 流量分析 主要以工控流量和恶意流量为主,难度较低的题目主要考察Wireshark使用和找规律,难度较高的题目主要考察协议定义和特征 简单只能简单得干篇一律,难可以难得五花八门 常见的工控协议有:Modbus、MMS、IEC60870、…

PyTorch笔记 - MAE(Masked Autoencoders) PyTorch源码

欢迎关注我的CSDN:https://blog.csdn.net/caroline_wendy 本文地址:https://blog.csdn.net/caroline_wendy/article/details/128382935 Paper:MAE - Masked Autoencoders Are Scalable Vision Learners 掩码的自编码器是可扩展的视觉学习器 Kaiming He,FAIR Code:https://…

English Learning - L1-5 从此口语变得简约(上)2022.12.19 周一

English Learning - L1-5 从此口语变得简约(上)2022.12.19 周一6 非谓语动词6.1 建设非谓语的知道思相6.2 非谓语的三驾马车6.2.1 不定式做主语不定式自己的主语作宾语作补语特殊句型6.2.1 分词作状语作定语6 非谓语动词 6.1 建设非谓语的知道思相 总纲…

Pycharm下载与安装

今天继续给大家介绍Python相关知识,本文主要内容是Pycharm下载与安装。 一、Pycharm简介 Pycharm是一款Python集成开发环境,拥有项目管理、代码高亮、调试、智能提示等功能。Pycharm是由JetBrains公司打造,该公司官网主页URL为:…

python--Django框架

文章目录一、预备知识MVC和MTV模式二、Django框架启用后台admin站点管理项目的数据库模型自定义模型加入后台管理Django请求的生命周期一、预备知识 HTTP协议: ------超文本传输协议,基于TCP/IP;HTTP属于应用层的面向对象的协议 ------基于请…

MySQL#3(约束,数据库设计,多表查询,事务)

目录 一.约束 二.数据库设计 1.数据库设计的概念 2.数据库设计的步骤 3.表关系 三.多表查询 1.内连接和外连接 2.子查询 四.事务 1.简介 2.事务语法 3.事务四大特征 一.约束 1.约束的概念 约束是作用于表中列上的规则,用于限制加入表的数据约束的存在保证…

java-线程池

1、线程池的自我介绍: ● 线程的数量过多会反复的创建并销毁 ● 为什么使用线程池? ○ 第一:反复创建线程开销大 ○ 第二:过多的线程会占用太多内存 解决以上两个问题的思路: ○ 用少量的线程-避免内存占用过多 ○ 让这…

重磅干货!一文读懂「企业级架构」

本文来自: 刘剑桥 极狐(GitLab) 高级解决方案架构师 首先来看两个真实的小故事: 1 小 A 公司有 50 人,作为运维人员,小 A 为公司搭建了一个私有化 GitLab 社区版。 某日,开发同学发现不能够访问 GitLab 了。小 A 查…

Web3中文|随着世界杯结束,web3体育可能达到800亿美元

随着卡塔尔世界杯的结束,Web3 Studios发布了一份对web3体育的深度报道。它声称该行业正处于发展拐点,到2030年其规模有可能达到800亿美元。 该报告数据贡献者来自Animoca Brands、FaceIt、ConsenSys、The Football Company、Apex Capital、Upland、Loot…

在Windows端使用XShell、WinSCP连接WSL2(win10的linux子系统/Ubuntu)

一、安装SSH服务 1、在ubuntu中输入: sudo apt-get install openssh-server 2、开启ssh服务 sudo service ssh start二、在Windows Subsystem for Linux (WSL2)中自动启动ssh服务 WSL2是Windows 10中提供的Linux子系统,基于Ubuntu内核。…

技术开发103

技术开发103 业务内容: 汽车音响等汽车电子部件试制、电子设备部件试制、精密钣金试制精密钣金试制 公司简介: 代表:中山尚美 成立时间:1950年6月 资本金:1000万日元 员工数:15名 资格认证/ S140001…

Python 实现将文本中数据批量写到Excel

欢迎小伙伴的点评✨✨,Python 常规应该开发系列,会渐进更新,如有需求,可留言🛒🛒🛒, 博主定当以每秒120赫兹的速度✈✈🚅🚅,写好博客。 文章目录前…

【408篇】C语言笔记-第十五章( 考研必会的查找算法考研真题实战)

文章目录第一节:顺序查找原理及实战1. 顺序查找原理解析2. 顺序查找代码实战第二节:折半查找原理及实战1. 折半查找原理解析2. 折半查找代码实战第三节:二叉排序树原理及建树实战1. 二叉排序树原理解析2. 二叉排序树代码实战第四节&#xff1…

Docker应用部署迁移备份DockerFile

文章标题一、应用部署1)MySQL部署2)Redis部署3)Nginx部署二、迁移与备份1)容器做成镜像2)把镜像被分成压缩包三、DockerFile1)基于Centos创建镜像自带Vim软件2)制作Django项目镜像一、应用部署 …

计算机毕设Python+Vue学生学科竞赛管理系统(程序+LW+部署)

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…

【第十三章 MySQL管理(mysqladmin,mysqlbinlog,mysqlshow,mysqldump,mysqlimport/source)】

第十三章 MySQL管理(mysqladmin,mysqlbinlog,mysqlshow,mysqldump,mysqlimport/source) 1.系统数据库: Mysql数据库安装完成后,自带了以下四个数据库,具体作用如下: 2.mysql: 该mysql不是指mys…

计算机毕设Python+Vue学院校友信息管理系统的(程序+LW+部署)

项目运行 环境配置: Jdk1.8 Tomcat7.0 Mysql HBuilderX(Webstorm也行) Eclispe(IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持)。 项目技术: SSM mybatis Maven Vue 等等组成,B/S模式 M…