3. 说说Java“锁“事

news2024/12/29 2:05:37

3.1 从轻松的乐观锁和悲观锁开讲

● 悲观锁: 认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改,synchronized和Lock的实现类都是悲观锁,适合写操作多的场景,先加锁可以保证写操作时数据正确,显示的锁定之后再操作同步资源-----狼性锁

● 乐观锁: 认为自己在使用数据的时候不会有别的线程修改数据或资源,不会添加锁,Java中使用无锁编程来实现,只是在更新的时候去判断,之前有没有别的线程更新了这个数据,如果这个数据没有被更新,当前线程将自己修改的数据成功写入,如果已经被其他线程更新,则根据不同的实现方式执行不同的操作,比如:放弃修改、重试抢锁等等。判断规则有:版本号机制Version,最常采用的是CAS算法,Java原子类中的递增操作就通过CAS自旋实现的。-----适合读操作多的场景,不加锁的特性能够使其读操作的性能大幅提升,乐观锁则直接去操作同步资源,是一种无锁算法,得之我幸不得我命—佛系锁
3.2 通过8种情况演示锁运行案例,看看锁到底是什么
3.2.1 锁相关的8种案例演示code

package com.bilibili.juc.locks;


import java.util.concurrent.TimeUnit;

class Phone //资源类
{
    public static synchronized void sendEmail()
    {
        try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("-----sendEmail");
    }

    public synchronized void sendSMS()
    {
        System.out.println("-----sendSMS");
    }

    public void hello()
    {
        System.out.println("-------hello");
    }
}

/**
 * 题目:谈谈你对多线程锁的理解,8锁案例说明
 * 口诀:线程   操作  资源类
 * 8锁案例说明:
 * 1 标准访问有ab两个线程,请问先打印邮件还是短信
 * 2 sendEmail方法中加入暂停3秒钟,请问先打印邮件还是短信
 * 3 添加一个普通的hello方法,请问先打印邮件还是hello
 * 4 有两部手机,请问先打印邮件还是短信
 * 5 有两个静态同步方法,有1部手机,请问先打印邮件还是短信
 * 6 有两个静态同步方法,有2部手机,请问先打印邮件还是短信
 * 7 有1个静态同步方法,有1个普通同步方法,有1部手机,请问先打印邮件还是短信
 * 8 有1个静态同步方法,有1个普通同步方法,有2部手机,请问先打印邮件还是短信
 *
 * 笔记总结:
 * 1-2
 *  *  *  一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,
 *  *  *  其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一的一个线程去访问这些synchronized方法
 *  *  *  锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
 *  3-4
 *  *  加个普通方法后发现和同步锁无关
 *  *  换成两个对象后,不是同一把锁了,情况立刻变化。
 *
 *  5-6 都换成静态同步方法后,情况又变化
 *  三种 synchronized 锁的内容有一些差别:
 * 对于普通同步方法,锁的是当前实例对象,通常指this,具体的一部部手机,所有的普通同步方法用的都是同一把锁——>实例对象本身,
 * 对于静态同步方法,锁的是当前类的Class对象,如Phone.class唯一的一个模板
 * 对于同步方法块,锁的是 synchronized 括号内的对象
 *
 * *  7-8
 *  *    当一个线程试图访问同步代码时它首先必须得到锁,正常退出或抛出异常时必须释放锁。
 *  *  *
 *  *  *  所有的普通同步方法用的都是同一把锁——实例对象本身,就是new出来的具体实例对象本身,本类this
 *  *  *  也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁。
 *  *  *
 *  *  *  所有的静态同步方法用的也是同一把锁——类对象本身,就是我们说过的唯一模板Class
 *  *  *  具体实例对象this和唯一模板Class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞态条件的
 *  *  *  但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。
 */
public class Lock8Demo
{
    public static void main(String[] args)//一切程序的入口
    {
        Phone phone = new Phone();
        Phone phone2 = new Phone();

        new Thread(() -> {
            phone.sendEmail();
        },"a").start();

        //暂停毫秒,保证a线程先启动
        try { TimeUnit.MILLISECONDS.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }

        new Thread(() -> {
            //phone.sendSMS();
            //phone.hello();
            phone2.sendSMS();
        },"b").start();
    }
}





/**
 *
 * ============================================
 *  1-2
 *  *  一个对象里面如果有多个synchronized方法,某一个时刻内,只要一个线程去调用其中的一个synchronized方法了,
 *  *  其它的线程都只能等待,换句话说,某一个时刻内,只能有唯一的一个线程去访问这些synchronized方法
 *  *  锁的是当前对象this,被锁定后,其它的线程都不能进入到当前对象的其它的synchronized方法
 *
 *  3-4
 *  *  加个普通方法后发现和同步锁无关
 *  *  换成两个对象后,不是同一把锁了,情况立刻变化。
 *
 *  5-6 都换成静态同步方法后,情况又变化
 *  三种 synchronized 锁的内容有一些差别:
 * 对于普通同步方法,锁的是当前实例对象,通常指this,具体的一部部手机,所有的普通同步方法用的都是同一把锁——>实例对象本身,
 * 对于静态同步方法,锁的是当前类的Class对象,如Phone.class唯一的一个模板
 * 对于同步方法块,锁的是 synchronized 括号内的对象
 *
 *  7-8
 *    当一个线程试图访问同步代码时它首先必须得到锁,正常退出或抛出异常时必须释放锁。
 *  *
 *  *  所有的普通同步方法用的都是同一把锁——实例对象本身,就是new出来的具体实例对象本身,本类this
 *  *  也就是说如果一个实例对象的普通同步方法获取锁后,该实例对象的其他普通同步方法必须等待获取锁的方法释放锁后才能获取锁。
 *  *
 *  *  所有的静态同步方法用的也是同一把锁——类对象本身,就是我们说过的唯一模板Class
 *  *  具体实例对象this和唯一模板Class,这两把锁是两个不同的对象,所以静态同步方法与普通同步方法之间是不会有竞态条件的
 *  *  但是一旦一个静态同步方法获取锁后,其他的静态同步方法都必须等待该方法释放锁后才能获取锁。
 **/





3.2.2 synchronized有三种应用方式
● 作用于实例方法,当前实例加锁,进入同步代码块前要获得当前实例的锁;
● 作用于代码块,对括号里配置的对象加锁
● 作用于静态方法,当前类加锁,进去同步代码前要获得当前类对象的锁

3.3 公平锁和非公平锁
3.3.1 何为公平锁/非公平锁
● 公平锁:是指多个线程按照申请锁的顺序来获取锁,这里类似于排队买票,先来的人先买,后来的人再队尾排着,这是公平的----- Lock lock = new ReentrantLock(true)—表示公平锁,先来先得。
● 非公平锁:是指多个线程获取锁的顺序并不是按照申请的顺序,有可能后申请的线程比先申请的线程优先获取锁,在高并发环境下,有可能造成优先级反转或者饥饿的状态(某个线程一直得不到锁)---- Lock lock = new ReentrantLock(false)—表示非公平锁,后来的也可能先获得锁,默认为非公平锁。
面试题:
● 为什么会有公平锁/非公平锁的设计?为什么默认非公平?
○ 恢复挂起的线程到真正锁的获取还是有时间差的,从开发人员来看这个时间微乎其微,但是从CPU的角度来看,这个时间差存在的还是很明显的。所以非公平锁能更充分地利用CPU的时间片,尽量减少CPU空间状态时间。
○ 使用多线程很重要的考量点是线程切换的开销,当采用非公平锁时,当一个线程请求锁获取同步状态,然后释放同步状态,所以刚释放锁的线程在此刻再次获取同步状态的概率就变得很大,所以就减少了线程的开销。
● 什么时候用公平?什么时候用非公平?
○ 如果为了更高的吞吐量,很显然非公平锁是比较合适的,因为节省了很多线程切换的时间,吞吐量自然就上去了;否则就用公平锁,大家公平使用。
3.3.2 预埋伏AQS
后续深入分析
3.4 可重入锁(递归锁)
3.4.1 概念说明
是指在同一线程在外层方法获取到锁的时侯,在进入该线程的内层方法会自动获取锁(前提,锁对象的是同一个对象),不会因为之前已经获取过还没释放而阻塞---------优点之一就是可一定程度避免死锁。
3.4.2 可重入锁种类
● 隐式锁(即synchronized关键字使用的锁),默认是可重入锁
○ 在一个synchronized修饰的方法或者代码块的内部调用本类的其他synchronized修饰的方法或者代码块时,是永远可以得到锁。
● 显式锁(即Lock)也有ReentrantLock这样的可重入锁


public class ReEntryLockDemo {

    public static void main(String[] args) {
        final Object o = new Object();
        /**
         * ---------------外层调用
         * ---------------中层调用
         * ---------------内层调用
         */
        new Thread(() -> {
            synchronized (o) {
                System.out.println("---------------外层调用");
                synchronized (o) {
                    System.out.println("---------------中层调用");
                    synchronized (o) {
                        System.out.println("---------------内层调用");
                    }
                }
            }
        }, "t1").start();

        /**
         * 注意:加锁几次就需要解锁几次
         * ---------------外层调用
         * ---------------中层调用
         * ---------------内层调用
         */
        Lock lock = new ReentrantLock();
        new Thread(() -> {
            lock.lock();
            try {
                System.out.println("---------------外层调用");
                lock.lock();
                try {
                    System.out.println("---------------中层调用");
                    lock.lock();
                    try {
                        System.out.println("---------------内层调用");
                    } finally {
                        lock.unlock();
                    }
                } finally {
                    lock.unlock();
                }
            } finally {
                lock.unlock();
            }
        }, "t2").start();
    }
}

3.5 死锁及排查
3.5.1 概念
死锁是指两个或两个以上的线程在执行过程中,因抢夺资源而造成的一种互相等待的现象,若无外力干涉,则它们无法再继续推进下去。
产生原因:
● 系统资源不足
● 进程运行推进顺序不合适
● 系统资源分配不当
在这里插入图片描述
3.5.2 写一个死锁代码case


public class DeadLockDemo {
    static  Object a=new Object();
    static  Object b=new Object();

    public static void main(String[] args) {
        new Thread(() -> {
            synchronized (a){
                System.out.println("t1线程持有a锁,试图获取b锁");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (b){
                    System.out.println("t1线程获取到b锁");
                }
            }
         },"t1").start();

        new Thread(() -> {
            synchronized (b){
                System.out.println("t2线程持有a锁,试图获取a锁");
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (a){
                    System.out.println("t2线程获取到a锁");
                }
            }
        },"t2").start();
    }
}

3.5.3 如何排查死锁
● 纯命令
○ jps -l
○ jstack 进程编号
● 图形化
○ jconsole

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

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

相关文章

WhaleHiking的“三山五岳”第一站:泰山

Datawhale团队 来源:whaleHiking 开场白 大家应该都听说过杜甫的《望岳》——“会当凌绝顶,一览众山小。”,每次看见这句诗的时候,总想去泰山看一看,感受诗中的壮美山河景色! 机不可失时不再来&#xff0c…

JVM图解

JVM图解 一、JVM的运行时数据区 概览JVM运行时数据区主要包括以下几个部分:程序计数器、虚拟机栈、本地方法栈、方法区、堆;其中 栈是运行时的单位,而堆是存储的单位! 1.程序计数器 程序计数器可以看作是当前线程所执行的字节码…

调用阿里API实现银行卡实名认证

调用阿里API实现银行卡实名认证 1.作者介绍2.算法介绍2.1 阿里云技术介绍2.2 API介绍 3.实验代码3.1 调用阿里云API流程3.2 代码实现3.2.1完整代码3.2.2实验结果 4.报错分析5.参考链接 1.作者介绍 薛维哥&…

针对潜在客户CRM如何进行管理?

CRM客户管理系统可以帮助企业管理客户,提高客户转化率,增加企业收入和利润。那么,在CRM中如何进行潜在客户管理呢?本文将从以下几个方面来为您解答。 一、什么是潜在客户? 潜在客户是指对企业的产品或服务有意向&…

驱动开发:内核遍历文件或目录

在笔者前一篇文章《驱动开发:内核文件读写系列函数》简单的介绍了内核中如何对文件进行基本的读写操作,本章我们将实现内核下遍历文件或目录这一功能,该功能的实现需要依赖于ZwQueryDirectoryFile这个内核API函数来实现,该函数可返…

docker的资源控制

一、CPU控制 cgroups,是一个非常强大的linux内核工具,他不仅可以限制被namespace隔离起来的资源, 还可以为资源设置权重、计算使用量、操控进程启停等等。所以cgroups (Control groups) 实现了对资源的配额和度量。 cgroups有四大功能: 资源限制:可以…

报表生成器FastReport .Net用户指南:“Picture“对象

FastReport .Net是一款全功能的Windows Forms、ASP.NET和MVC报表分析解决方案,使用FastReport .NET可以创建独立于应用程序的.NET报表,同时FastReport .Net支持中文、英语等14种语言,可以让你的产品保证真正的国际性。 FastReport.NET官方版…

【30天熟悉Go语言】6 Go 复杂数据类型之指针

文章目录 一、前言二、数据类型总览三、指针1、特殊运算符& *2、内存角度来看指针3、使用指针修改数据4、指针使用的注意事项5、对比着看Java的引用类型 三、总结 一、前言 Go系列文章: GO开篇:手握Java走进Golang的世界2 Go开发环境搭建、Hello Wor…

Zstack实习-基础知识总结归纳-待更新

什么是虚拟化? 虚拟化技术是一种将物理计算资源,如服务器、存储和网络等,转化成虚拟的逻辑资源的技术。通过虚拟化技术,可以将多个独立的操作系统运行在同一台物理计算机上,实现资源的共享,提高硬件的利用率…

C++那些事之项目篇Catch2

C那些事之项目篇Catch2 今天推荐一个值得学习的开源项目"Catch2" ,之前写过如何使用google的googletest编写单元测试,你会发现需要编译生成lib库,比较麻烦,而Catch2是一个Header only库,能够快速使用&#x…

Python3数据分析与挖掘建模(13)复合分析-因子关分析与小结

1.因子分析 1.1 探索性因子分析 探索性因子分析(Exploratory Factor Analysis,EFA)是一种统计方法,用于分析观测变量之间的潜在结构和关联性。它旨在确定多个观测变量是否可以归结为较少数量的潜在因子,从而帮助简化…

机器学习笔记 - 多实例学习(MIL)弱监督学习

一、多实例学习概述 多实例学习(MIL)是一种弱监督学习形式,其中训练实例被排列在称为袋的集合中,并为整个袋提供标签。这种方式越来越受到人们的关注,因为它自然适合各种问题,并允许利用弱标记数据。因此,它被应用于计算机视觉和文档分类等不同的应用领域。 多实例学习(…

【国产虚拟仪器】基于 ZYNQ的声发射采集系统方案

系 统 的 整 体 设 计 流 程 为 根 据 上 述 设 计 需 求 进 行 硬 件 设 备 的 选 型 并 搭 建 起 声 发 射 采 集 系 统 的 硬 件 平 台 , 在 该平 台 上 进行 声 发 射 采 集 板 卡 的 设 计 并 根 据 要求进行 上 位 机…

2023上半年的九个觉悟

‍觉悟,就是觉了、悟了。有时候,你看到一句话,突然就觉悟了。 一、资本主义的问题 “资本主义把我们都缩减成了一个东西:消费者” 因此,人人都成为资本家利诱、操控、围猎的对象。 同时,金钱成为全民的神&a…

【PHP】Wampserver启动时呈现黄色的解决方案

首先,我们可以进入服务查看WampServer的服务是哪几个没有开? 经观察上图发现: apache没有开启sql没有开启 如果你点击开启,将会出现报错信息,因为服务冲突 所以解决方式就是:关闭冲突服务,再次…

基于SpringBoot+Vue的校园台球厅人员与设备管理系统设计与实现

博主介绍: 大家好,我是一名在Java圈混迹十余年的程序员,精通Java编程语言,同时也熟练掌握微信小程序、Python和Android等技术,能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架下…

基于JavaWeb SSM mybatis 学生信息管理系统设计和实现以及文档报告

基于JavaWeb SSM mybatis 学生信息管理系统设计和实现以及文档报告 博主介绍:5年java开发经验,专注Java开发、定制、远程、指导等,csdn特邀作者、专注于Java技术领域 作者主页 超级帅帅吴 Java项目精品实战案例《500套》 欢迎点赞 收藏 ⭐留言 文末获取源…

想要更好地理解大模型架构?从计算参数量快速入手

编者按:要理解一种新的机器学习架构(以及其他任何新技术),最有效的方法就是从头开始实现它。然而,还有一种更简单的方法——计算参数数量。 通过计算参数数量,读者可以更好地理解模型架构,并检查…

LBP特征提取

文章目录 1 LBP原理及其扩展1.1 原始LBP1.2 圆形LBP1.3 旋转不变LBP1.4 Uniform Pattern LBP1.5 MB-LBP 2. 对于边缘点的处理3. LBP的Python应用4. LBPH算法及其应用4.1 LBPH算法原理4.2 LBPH识别应用 5. 源码仓库地址 LBP指局部二值模式,英文全称:Local…

3.1 基于配置文件整合SSM框架实现用户登录

一、基于XML配置方式整合SSM框架实现用户登录 1、创建数据库与表 (1)创建数据库 数据库simonshop,采用utf8mb4编码 (2)创建用户表 创建用户表t_user CREATE TABLE t_user (id int(11) NOT NULL AUTO_INCREMENT,u…