【JUC中的 “8锁现象” 】 —— 每天一点小知识

news2024/12/28 18:28:06

在这里插入图片描述

                                                                              💧 J U C 中的“ 8 锁现象” \color{#FF1493}{JUC中的 “8锁现象” } JUC中的“8锁现象💧          


🌷 仰望天空,妳我亦是行人.✨
🦄 个人主页——微风撞见云的博客🎐
🐳 《数据结构与算法》专栏的文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺
💧 《Java学习笔记》专栏的文章是本人在Java学习中总结的一些知识点~ 💐
🥣 《每天一点小知识》专栏的文章可以丰富你的知识库,滴水成河~ 🌊
🪁 希望本文能够给读者带来一定的帮助~🌸文章粗浅,敬请批评指正!🐥


文章目录

  • JUC中的8锁现象
    • 🌊什么是8锁现象?
    • 🌊为什么会产生8锁现象?
    • 🌊如何避免8锁现象?
    • 🌊狂神谈 “8锁现象”
      • 案例一(先打印“发短信”)
      • 案例二(先打印“打电话”)
      • 案例三(先打印“发短信”)
      • 案例四(先打印“打电话”)
    • 🌊总结一下
  • 🐳结语


JUC中的8锁现象

    💧在Java并发编程中,是一种关键的同步机制,用于控制多个线程对共享资源的访问。然而,在某些情况下,使用锁可能会引发性能问题,其中一个典型例子就是JUC中的8锁现象,也被称为锁粗化

🌊什么是8锁现象?

    💧8锁现象指的是在多线程环境下,当多个线程同时同一个对象不同锁进行操作时,可能导致性能下降的现象。每个对象都有一个与之关联的监视器锁,也称为内置锁互斥锁。当一个线程试图进入一个同步代码块时,它需要先获得该对象的监视器锁。如果锁已经被另一个线程获得,则线程将被阻塞,直到锁被释放。

    💧并发编程中,当多个线程竞争同一个对象的不同锁时,会产生线程切换竞争的开销,从而导致性能下降。这是因为每个对象都有自己的锁,并且每个锁都有自己的计数器等待队列当多个线程同时竞争同一个对象上的不同锁时,会频繁地发生线程切换,造成性能瓶颈

🌊为什么会产生8锁现象?

    💧8锁现象的产生与Java虚拟机(JVM)锁的优化机制有关。在某些情况下,JVM会尝试对连续的同步块进行优化,将多个锁的获取和释放合并为一个更大的同步块,从而减少线程之间的竞争和切换。这个过程称为锁粗化

    💧然而,当多个线程对同一个对象的不同锁进行操作时,JVM无法进行锁粗化优化每个锁都需要独立获取和释放,导致频繁线程切换和竞争,从而降低并发性能

🌊如何避免8锁现象?

    💧为了避免8锁现象带来的性能问题,可以尝试以下优化策略

  1. 合并锁: 如果多个锁操作的是同一个对象,可以考虑将它们合并成一个锁通过合并相同对象的多个锁,减少锁的竞争,从而提高程序的并发性能。这需要仔细设计代码结构,确保多个锁的操作在合并后仍然保持独立性和正确性。

  2. 细粒度锁: 通过将对象分解成更小的粒度可以减小锁的粒度。如果可能,可以将一个大的共享对象拆分为多个小的独立对象,每个对象使用自己的锁。这样可以使得多个线程可以同时访问不同的锁,减少竞争和线程切换的开销。细粒度锁同样需要对代码进行仔细的分析和设计,以确保线程安全和正确性。

  3. 使用并发集合类: Java提供了一些高效的并发集合类,如ConcurrentHashMapConcurrentLinkedQueue。这些并发集合类内部实现了细粒度的并发控制机制可以减少对锁的依赖,提高并发性能。通过使用这些并发集合类,可以避免手动管理锁带来的复杂性,同时保证线程安全和高效的并发访问。

  4. 使用并发框架: JUC(Java Util Concurrent)中提供了一些高级的并发编程框架,如线程池Future模式。通过使用这些框架,可以更好地管理线程和任务,提高并发性能和可扩展性。线程池可以重用线程,减少线程创建和销毁的开销;Future模式可以异步执行任务并获取结果,提高程序的并发度。

需要注意的是,优化锁的使用需要根据具体的业务场景和代码结构进行分析和调整。过度优化可能会引入新的问题或复杂性。在进行优化之前,建议进行性能测试和基准测试,以确保优化策略的有效性和稳定性。


🌊狂神谈 “8锁现象”

8锁,就是关于锁的8个问题。如何判断锁的是谁!永远的知道什么锁,锁到底锁的是谁!

下面通过 4 个案例来直观的感受一下 “8锁现象”

案例一(先打印“发短信”)

  • 标准情况下,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话。
  • sendSms延迟4秒,两个线程先打印 发短信还是 打电话? 1/发短信 2/打电话。
public class Test1 {
    public static void main(String[] args) {
        Phone phone = new Phone();

        //锁的存在
        new Thread(()->{
            phone.sendSms();
        },"A").start();

        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone.call();
        },"B").start();
    }
}

class Phone{

    // synchronized 锁的对象是方法的调用者!、
    // 两个方法用的是同一个锁,谁先拿到谁执行!
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }

}

    💧案例一总结:synchronized 锁的对象是方法的调用者,两个方法用的是同一个锁,谁先拿到谁执行。

案例二(先打印“打电话”)

  • 增加了一个普通方法后!先执行发短信还是Hello? 普通方法。
  • 两个对象,两个同步方法, 发短信还是 打电话? // 打电话
public class Test2  {
    public static void main(String[] args) {
        // 两个对象,两个调用者,两把锁!
        Phone2 phone1 = new Phone2();
        Phone2 phone2 = new Phone2();

        //锁的存在
        new Thread(()->{
            phone1.sendSms();
        },"A").start();

        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

class Phone2{

    // synchronized 锁的对象是方法的调用者!
    public synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public synchronized void call(){
        System.out.println("打电话");
    }

    // 这里没有锁!不是同步方法,不受锁的影响
    public void hello(){
        System.out.println("hello");
    }

}

    💧案例二总结:synchronized 锁的对象是方法的调用者,hello() 没有锁!不是同步方法,所以不受锁的影响。

案例三(先打印“发短信”)

  • 增加两个静态的同步方法,只有一个对象,先打印 发短信?打电话?
  • 两个对象!增加两个静态的同步方法, 先打印 发短信?打电话?
public class Test3  {
    public static void main(String[] args) {
        // 两个对象的Class类模板只有一个,static,锁的是Class
        Phone3 phone1 = new Phone3();
        Phone3 phone2 = new Phone3();

        //锁的存在
        new Thread(()->{
            phone1.sendSms();
        },"A").start();

        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

// Phone3唯一的一个 Class 对象
class Phone3{

    // synchronized 锁的对象是方法的调用者!
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    public static synchronized void call(){
        System.out.println("打电话");
    }


}

    💧案例三总结:两个对象的Class类模板只有一个。static 静态方法在类一加载就有了!锁的是Class。

案例四(先打印“打电话”)

  • 1个静态的同步方法,1个普通的同步方法 ,一个对象,先打印 发短信?打电话?
  • 1个静态的同步方法,1个普通的同步方法 ,两个对象,先打印 发短信?打电话?
public class Test4  {
    public static void main(String[] args) {
        // 两个对象的Class类模板只有一个,static,锁的是Class
        Phone4 phone1 = new Phone4();
        Phone4 phone2 = new Phone4();
        //锁的存在
        new Thread(()->{
            phone1.sendSms();
        },"A").start();

        // 捕获
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(()->{
            phone2.call();
        },"B").start();
    }
}

// Phone3唯一的一个 Class 对象
class Phone4{

    // 静态的同步方法 锁的是 Class 类模板
    public static synchronized void sendSms(){
        try {
            TimeUnit.SECONDS.sleep(4);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("发短信");
    }

    // 普通的同步方法  锁的调用者
    public synchronized void call(){
        System.out.println("打电话");
    }

}

    💧案例四总结:静态的同步方法 锁的是 Class 类模板,普通的同步方法 锁的调用者。

new this 具体的一个手机

static Class 唯一的一个模板


🌊总结一下

    💧在Java并发编程中,8锁现象是指当多个线程对同一个对象的不同锁进行操作时可能导致的性能下降。通过合并锁、细粒度锁、使用并发集合类和并发框架等优化策略,可以避免8锁现象带来的性能问题,提高程序的并发性能和可扩展性。

    💧了解并应用这些优化策略,可以帮助我们编写高效、可靠的并发程序。然而,需要根据具体场景进行分析和调整,避免过度优化和引入新的问题。在实际开发中,综合考虑性能、可维护性和代码可读性,选择适合的优化策略,并进行充分测试和验证。


在这里插入图片描述


🐳结语

🐬初学一门技术时,总有些许的疑惑,别怕,它们是我们学习路上的点点繁星,帮助我们不断成长。

🐟积少成多,滴水成河。文章粗浅,希望对大家有帮助!

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

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

相关文章

python基础学习3【NumPy矩阵与通用函数【矩阵相乘+平方+广播机制+转置】+ save、load、sort、repeat、unique、鸢尾花1】

NumPy矩阵与通用函数 a np.mat([[1,2],[3,4]])#生成矩阵b np.matrix([[1,7],[6,4]])np.bmat("a b") 矩阵的运算 矩阵特有属性: 属性 说明 T自身转置H共轭转置I逆矩阵A自身数据的二维数据视图 例如: np.matrix(a).T 矩阵相乘:…

【Map集合的基本功能】

Map集合的基本功能 V put(K key,V value):添加元素 V remove(K key,V value):根据键删除键值对元素 package com.gather.map; //Map集合常用的基本功能 import java.util.HashMap; import java.util.Map; public class MapDemo02 {public static void m…

Flutter:路由、拦截返回事件、自定义独立路由

路由堆栈 Flutter 路由管理中有两个非常重要的概念: Route:路由是应用程序页面的抽象,对应 Android 中 Activity 和 iOS 中的 ViewController,由 Navigator 管理。Navigator:Navigator 是一个组件,管理和…

哈希表--想彻底了解哈希表,看这一篇文章就可以了

为了一次存储便能得到所查记录,在记录的存储位置和它的关键字之间建立一个确定的对应关系H,已H(key)作为关键字为key的记录在表中的位置,这个对应关系H为哈希(Hash)函数, 按这个思路建立的表为哈…

【Leetcode】 1071. 字符串的最大公因子

For two strings s and t, we say “t divides s” if and only if s t ... t (i.e., t is concatenated with itself one or more times). Given two strings str1 and str2, return the largest string x such that x divides both str1 and str2. Example 1: Input: s…

开发改了接口,经常忘通知测试的解决方案!

目录 前言: Apifox解决方案 Apifox对此给出的解决方案是: 用Apifox怎么处理接口变更 接口代码实现逻辑修改 接口参数修改 前言: 在开发过程中,接口变动十分频繁,测试人员没有及时获得相关通知的情况也很普遍。这…

这一次AI应该是真正的已经到来

渐渐感觉这一次AI的变革能真正的突破迷雾,迎来真正的人工智能时代的到来。所以写篇博文学习一下。经过半年的发酵与发展,不得不说AI已经成为了不可逆转的趋势。之所以说这一次AI应该是真正的已经到来,是因为人工智能的发展其实已经经历了几十…

【Python】一文带你了解 正则表达式 + 简单操作

作者主页:爱笑的男孩。的博客_CSDN博客-深度学习,活动,python领域博主爱笑的男孩。擅长深度学习,活动,python,等方面的知识,爱笑的男孩。关注算法,python,计算机视觉,图像处理,深度学习,pytorch,神经网络,opencv领域.https://blog.csdn.net/Code_and516?typeblog个…

通过第三方软件修改 MacOS 的键盘映射

通过第三方软件修改 MacOS 的键盘映射 由于文本编辑时大量使用word level的左移、右移,其中: OSX的单词级左右移为option Left/Right Arrow,整行级左右移为command Left/Right Arrow 单词级移动与进行编辑常用的command不同键位&#xff0c…

Chat2DB:阿里巴巴开源的聊天数据管理工具--实践

Chat2DB:阿里巴巴开源的聊天数据管理工具–实践 简介 ​ Chat2DB 是一款有开源免费的多数据库客户端工具,支持windows、mac本地安装,也支持服务器端部署,web网页访问。和传统的数据库客户端软件Navicat、DBeaver 相比Chat2DB集成了…

Jetpack Compose ——Row

当我们构建界面时,经常需要在Compose中使用Row布局来水平排列多个组件。Row提供了一种方便的方式来管理和定位子组件,使它们按照我们期望的方式呈现。 在Compose中,Row可以接受多个子组件作为参数,并根据指定的布局规则进行排列。…

桥梁监测方案-智慧桥梁监测管理系统

桥梁作为现代交通基础设施的重要组成部分,承载着大量的车辆和行人交通。然而,随着时间的推移,桥梁结构可能会受到自然环境、交通负荷和物质疲劳等因素的影响,从而导致潜在的风险和结构损坏。桥梁监测能够通过实时监测桥梁的结构状…

git一定要学会,加油

gitgit文档http://file:///F:/%E8%B5%84%E6%96%99%E5%A4%8D%E4%B9%A0/Git%E4%BC%98%E7%A7%80%E5%BC%80%E6%BA%90%E4%B9%A6%E7%B1%8D/Git%E5%BC%80%E6%BA%90%E4%B9%A6%E7%B1%8D/Pro%20Git%E4%B8%AD%E6%96%87PDF%E7%89%88.pdf init 初始化仓库 这个命令在当前目录下初始化一个 G…

安装Django 3.2的过程记录~

首先看下Django 3.2目前更新到哪些版本,用下面这条命令: pip install Django100.0.5与3.2有关的版本号有: 3.2a1, 3.2b1, 3.2rc1, 3.2, 3.2.1, 3.2.2, 3.2.3, 3.2.4, 3.2.5, 3.2.6, 3.2.7, 3.2.8, 3.2.9, 3.2.10, 3.2.11, 3.2.12, 3.2.13, …

MAC系统使用

查看端口占用情况 //如查看8080端口的占用情况 sudo lsof -i tcp:9901//比如我们想要释放Java占用的9901端口 PID 是 12420 kill -9 12420

C语言:写一个函数,实现一个整型有序数组的二分查找

题目: 写一个函数,实现一个整型有序数组的二分查找,找出要找的数字在数组中对应的下标。 思路: 总体思路: (一)自定义函数部分: (1). 参数: int…

融云《社交泛娱乐出海作战地图》,手把手教你拿下出海“最后一公里”难题

社交泛娱乐出海,已然是当下互联网出海的主力。关注【融云全球互联网通信云】了解更多 不少国内互联网巨头都将社交泛娱乐作为海外市场重点布局赛道。从较为传统的音视频、短视频社交出海,到逐渐直播出海、元宇宙社交出海,社交泛娱乐出海版图…

微信小程序实现tab及瀑布流页面

一、效果 二、代码 复制代码可以直接用 1、json代码 {"usingComponents": {},"navigationStyle": "custom" } 2、xml代码 <!--pages/find_module/findPage.wxml--> <view class"container_serach"><!-- 搜索模块 -…

Java EE 企业级应用开发教程题库(第二版)

前言 Java EE这是一门偏向于实践的课&#xff0c;奈何考试理论居多。一学期想搞懂三个框架&#xff0c;嘿嘿&#xff0c;难哦&#xff01;如果你是大一大二的同学&#xff0c;认认真真学习&#xff0c;真的有用。如果你是大三的同学&#xff0c;像就业并且走这个方向的同学&…

VMware Horizon 8 运维系列(一)windows系统重启被禁用的ipv6功能

前言 Horizon 8 在创建桌面池之前,需要对模板虚拟机进行配置,其中会用到windows优化工具,通过禁用一些不必要的服务或功能,让系统达到优化的效果。 最近客户提出一个需求:需要对虚拟机开启ipv6功能,并同时使用ipv6地址。通过查看模板系统,ipv6已经被优化工具禁用,所以需…