【JUC】四、可重入锁、公平锁、非公平锁、死锁现象

news2024/11/26 6:03:49

文章目录

  • 1、synchronized
  • 2、公平锁和非公平锁
  • 3、可重入锁
  • 4、死锁

1、synchronized

写个demo,具体演示下对象锁与类锁,以及synchronized同步下的几种情况练习分析。demo里有资源类手机Phone,其有三个方法,发短信和发邮件这两个方法有synchronized关键字,另一个普通方法getHello。然后启动两个线程AA和BB,且二者进入就绪状态中间休眠100ms,给AA一个先抢夺CPU时间片的优势。

public class Lock8 {

    public static void main(String[] args) throws InterruptedException {
        Phone phone = new Phone();
        new Thread(() -> {
            try {
                phone.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"AA").start();

        /**
         * start后线程进入的是就绪状态,即具有抢夺CPU时间片(执行权)的能力,并不是直接执行
         * 这里刻意休眠100毫秒,让AA线程先去抢时间片,给AA一个先执行的优势
         */
        Thread.sleep(100);

        new Thread(() -> {
            try {
                phone.sendEmail();
                //phone.getHello();
            } catch (Exception e) {
                e.printStackTrace();
            }
        },"BB").start();

    }

}
class Phone {
    public synchronized void sendSMS() throws Exception {
        //TimeUnit.SECONDS.sleep(4);
        System.out.println("------sendSMS");
    }

    public synchronized void sendEmail() throws Exception {
        System.out.println("------sendEmail");
    }

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

分析以下八种情况的输出结果:

Case1:就上面的代码,直接执行

Case2:sendSMS()方法体加一行TimeUnit.SECONDS.sleep(4),即停留4秒

分析:对于上面两种情况,synchronized出现在示例方法中占的是对象锁,而两线程共用同一个对象,因此先抢时间片的线程AA先执行,而sleep是抱着锁睡,所以输出都是:

------sendSMS
------sendEmail

Case3:BB线程改为调用普通方法getHello

分析:getHello方法不用对象锁,所以不用等,而AA线程的sendSMS要sleep4秒,因此getHello就先输出:

------getHello
------sendSMS

Case4:两个手机Phone对象,分别给AA和BB线程调用两个synchronized方法

分析:两个Phone对象,两个对象锁,各自调synchronized实例方法,没有抢锁和等待的情况,没有sleep的自然先输出:

------sendEmail
------sendSMS

Case5:两个synchronized方法均加static改为静态方法,两线程共用1个Phone资源对象

Case6:两个synchronized方法均加static改为静态方法,两线程分别用2个Phone资源对象

分析:synchronized两个静态方法,锁的就是类锁,即当前类的Class对象,一个类就一把类锁,所以尽管有两个Phone对象在调也没用,先拿到类锁的先执行并输出:

------sendSMS
------sendEmail

Case7:BB线程调用静态同步sendEmail、AA线程调用无static的同步方法sendSMS,两线程共用1个Phone资源对象

Case8:BB线程调用静态同步sendEmail、AA线程调用无static的同步方法sendSMS,两线程分别用2个Phone资源对象

分析:不管1个/2个Phone对象,AA线程用的对象锁,BB线程用的类锁,互不影响,对象锁代码中有sleep,晚输出:

------sendEmail
------sendSMS

总结:

synchronized实现同步时:

  • synchronized加在静态方法上,锁的就是类锁,即当前类的Class对象,一个类就一把类锁
  • synchronized加在实例方法上,锁的是调用该方法的当前对象,是对象锁,一个类创建100个对象,就有100把对象锁
  • synchronized加在代码块上,锁的是synchronized括号里配置的对象

2、公平锁和非公平锁

还是之前三个线程卖30张票的例子:

//资源类
class LTicket{

    private Integer number = 30;

    private final ReentrantLock lock = new ReentrantLock();

    public void sale(){
        //上锁
        lock.lock();
        try {
            if( number > 0 ){
                System.out.println(Thread.currentThread().getName() + ": 卖出票,剩余" + number--);
            }

        } finally {
            //释放锁写finally语句中,防止上面发生异常导致锁未释放
            lock.unlock();
        }
    }
}

开启三个线程,调用资源类的方法:

public class LSaleTicket {

    public static void main(String[] args) {
        LTicket ticket = new LTicket();
        new Thread(() -> {
            for(int i = 0 ; i < 40; i++ ){
                ticket.sale();
            }
        },"AA").start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                for(int i = 0 ; i < 40; i++ ){
                    ticket.sale();
                }
            }
        },"BB").start();
        new Thread(() -> {
            for (int i = 0; i < 40; i++) {
                ticket.sale();
            }
        }, "CC").start();
    }
}


执行会发现,经常AA一个线程就把所有票一个人卖完了:

在这里插入图片描述

这就是非公平锁,上面new Lock对象时:

private final ReentrantLock lock = new ReentrantLock();
//不传参数,默认非公平锁

源码:

在这里插入图片描述

改为公平锁:

private final ReentrantLock lock = new ReentrantLock(true);

看下效果:

在这里插入图片描述

总结:

  • 非公平锁:可能导致线程饿死,但效率高
  • 公平锁:阳光普照,但效率比非公平锁低

二者相比,就像去图书馆自习,非公平锁是看到座位就座,公平锁则是先问下这里有人吗,如果有,就排队,公平锁的源码:

在这里插入图片描述

3、可重入锁

synchronized( 隐式)和 Lock( 显式)都是可重入锁,这里的显式隐式即指的Lock需要开发者手动加锁解锁。可重入锁,理解为你家大门上有锁、卧室门上有锁、卫生间有锁,但只要你打开了大门的锁,卧室、卫生间就不需要再次开锁了,这些房间就可以自由进入了。类比到代码中,就是他们用的是同一把锁。可重入锁又叫递归锁。

在这里插入图片描述
写个synchronized同步代码块

public class SyncLockDemo {
    
    public static void main(String[] args) {
        
        Object o = new Object();
        new Thread(() -> {
            synchronized (o){
                System.out.println(Thread.currentThread().getName() + " 外层");
                synchronized (o){
                    System.out.println(Thread.currentThread().getName() + " 中层");
                    synchronized (o){
                        System.out.println(Thread.currentThread().getName() + " 内层");
                    }
                }
            }
        },"t1").start();

    }
}

在这里插入图片描述

关于可重入锁又叫递归锁:

public class SyncLockDemo {
    
    public synchronized void add(){
        add();
    }

    public static void main(String[] args) {
        
        new SyncLockDemo().add();
     
    }
}

在这里插入图片描述

可以看到递归调用synchronized实例方法add,会出现StackOverflowError,就可以说明可重入锁的特点,要是不可重入,那递归时再调add方法,就没对象锁给它用了。再用lock显示演示:

在这里插入图片描述

另外,lock与unlock必须成对,当然这里内层锁的unlock注释掉,也能运行成功,进入大门后你是可以自由活动的,但你少个unlock,后面线程再想lock,就等不到锁了,相当于你进门,自己休息好了再出来却不带钥匙就把门关了。

4、死锁

在这里插入图片描述
t1线程执行某同步代码块,用到了对象1的锁和对象2的锁,即t1线程需要先锁对象1,再锁对象2,全锁以后,算同步代码块执行结束,然后一下释放两个对象锁。(A加锁-B加锁-B解锁-A解锁)

t2线程执行另一个同步代码块,需要先锁对象2,再锁对象1才算这个同步代码块执行结束,然后释放两个对象锁。(B加锁-A加锁-A解锁-B解锁)

如此:t1锁到对象2的时候,发现已被锁,则等待,而另一边:t2锁到对象1的时候,发现对象1已被锁,两个线程同时陷入无休止的等待…尬住了。此时,若无外力干涉,就执行不下去了,表现在执行结果就是光标闪烁,无输出,但也没有执行结束。

产生死锁的原因:
  • 系统资源不足
  • 进程运行推进顺序不合理
  • 资源分配不当

写个死锁的Demo:

public class DeadLock {

    public static void main(String[] args) {
        Object o1= new Object();
        Object o2= new Object();

        new Thread(() -> {
            synchronized (o1){
                System.out.println(Thread.currentThread().getName()  + "===>持有o1对象锁,试图获取o2对象锁");
                try {
                    Thread.sleep(200);  //抱着o1锁睡会儿,别太快执行结束释放两个锁,以保证死锁必现
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o2){
                    System.out.println(Thread.currentThread().getName()  + "===>获取到o2对象锁");
                }
            }
        },"t1").start();

        new Thread(() -> {
            synchronized (o2){
                System.out.println(Thread.currentThread().getName()  + "===>持有o2对象锁,试图获取o1对象锁");
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (o1){
                    System.out.println(Thread.currentThread().getName()  + "===>获取到o1对象锁");
                }
            }
        },"t2").start();
    }

}

运行:

在这里插入图片描述

当然,不能一看到不exit0,也无输出就说是死锁,死循环、远程调用也可能有这个情况,关于是否是死锁的验证:

  • jps:类似Linux的ps -ef
  • jstack:JVM自带堆栈跟踪工具

关于jps:
在这里插入图片描述
没配环境变量,在IDEA终端先cd到这儿,再执行:

在这里插入图片描述

然后在IDEA终端继续执行:

jstack 10212

返回的关键信息:

在这里插入图片描述

关于死锁和线程安全的优化:

synchronized会让程序执行效率变低,系统吞吐量降低,用户体验变差。解决线程安全,可考虑:

  • 使用局部变量代替实例变量和静态变量
  • 若必须使用实例变量,考虑多创建几个对象,别对象共享了也就没有安全问题了
  • 若以上两条都做不到,则用synchronized

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

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

相关文章

加速可编程创新,2023年英特尔FPGA中国技术日披露全矩阵FPGA产品与应用方案

在新场景、新应用海量增长的驱动下&#xff0c;中国本地市场对于FPGA产品的需求也在日益多元化和快速扩展。我们始终致力于以中国客户的实际需求为导向&#xff0c;基于领先的FPGA产品和软件为千行百业提供全场景的解决方案。——叶唯琛 英特尔可编程方案事业部中国总经理 今日…

智能运维软件,提升效率的利器

随着信息技术的飞速发展&#xff0c;企业对于IT系统的依赖程度日益加深。为保障IT系统的稳定运行&#xff0c;越来越多的企业选择智能运维管理软件&#xff0c;以全面高效的监控和管理系统和资产情况。 一、运维监控平台的重要性 无监控&#xff0c;不运维。将资产并入监控系…

盘点72个ASP.NET Core源码Net爱好者不容错过

盘点72个ASP.NET Core源码Net爱好者不容错过 学习知识费力气&#xff0c;收集整理更不易。 知识付费甚欢喜&#xff0c;为咱码农谋福利。 链接&#xff1a;https://pan.baidu.com/s/1nlQLLly_TqGrs5O8eOmZjA?pwd8888 提取码&#xff1a;8888 项目名称 (Chinese) 物业收费…

敏捷开发中如何写好用户故事

写好用户故事是敏捷开发中非常重要的一环&#xff0c;它们是描述用户需求的核心。以下是一些关于如何编写优秀用户故事的建议&#xff1a; 使用标准模板&#xff1a; 一个常用的用户故事模板是“As a [用户角色]&#xff0c;I want [功能]&#xff0c;so that [价值]”。这种模…

flutter下拉列表

下拉列表 内容和下拉列表的标题均可滑动 Expanded&#xff1a; 内容限制组件&#xff0c;将其子类中的无限扩展的界面限制在一定范围中。在此使用&#xff0c;是为了防止下拉列表中的内容超过了屏幕限制。 SingleChildScrollView&#xff1a; 这个组件&#xff0c;从名字中可…

JavaScript中的原型和原型链

给大家推荐一个实用面试题库 1、前端面试题库 &#xff08;面试必备&#xff09; 推荐&#xff1a;★★★★★ 地址&#xff1a;web前端面试题库 原型和原型链是JavaScript中一个重要且常常被误解的概念。它们在理解对象、继承和属性查找时扮演着关键的角色。 1…

智慧城市项目建设介绍

1. 项目建设背景 随着城市化进程的加速&#xff0c;城市发展面临着诸多挑战&#xff0c;如环境污染、城镇综合管理、经济发展布局等。为了应对这些挑战&#xff0c;智慧城市应运而生&#xff0c;成为城市发展的重要方向。智慧城市通过运用信息技术和智能化技术&#xff0c;实…

腾讯云标准型S5服务器五年优惠价格表(4核8G和2核4G)

腾讯云服务器网整理五年云服务器优惠活动 txyfwq.com/go/txy 配置可选2核4G和4核8G&#xff0c;公网带宽可选1M、3M或5M&#xff0c;系统盘为50G高性能云硬盘&#xff0c;标准型S5实例CPU采用主频2.5GHz的Intel Xeon Cascade Lake或者Intel Xeon Cooper Lake处理器&#xff0c;…

(六)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB

一、七种算法&#xff08;DBO、LO、SWO、COA、LSO、KOA、GRO&#xff09;简介 1、蜣螂优化算法DBO 蜣螂优化算法&#xff08;Dung beetle optimizer&#xff0c;DBO&#xff09;由Jiankai Xue和Bo Shen于2022年提出&#xff0c;该算法主要受蜣螂的滚球、跳舞、觅食、偷窃和繁…

libgdx实现雪花、下雪效果(二十三)

libgdx实现雪花、下雪效果&#xff08;二十三&#xff09; 转自&#xff1a;https://lingkang.top/archives/libgdx-shi-xian-xue-hua package effect;import com.badlogic.gdx.ApplicationAdapter; import com.badlogic.gdx.Gdx; import com.badlogic.gdx.backends.lwjgl3.…

腾讯云4核8G和2核4G服务器五年优惠价格表

腾讯云百科整理五年云服务器优惠活动 txybk.com/go/txy 配置可选2核4G和4核8G&#xff0c;公网带宽可选1M、3M或5M&#xff0c;系统盘为50G高性能云硬盘&#xff0c;标准型S5实例CPU采用主频2.5GHz的Intel Xeon Cascade Lake或者Intel Xeon Cooper Lake处理器&#xff0c;睿频3…

【C++】join ()和detach ()函数详解和示例

简单的来说&#xff0c;join ()方法建立的线程具有阻碍作用&#xff0c;该线程不结束&#xff0c;另一些函数就无法运行。detach ()方法建立的线程&#xff0c;可以和另一些函数同时进行。下面以示例进行详细说明&#xff0c;以帮助大家理解和使用。 目录 join ()detach () jo…

Java —— 继承

目录 1. 为什么需要继承 2. 继承概念 3. 继承的语法 4. 父类成员访问 4.1 子类中访问父类的成员变量 1. 子类和父类不存在同名成员变量 2. 子类和父类成员变量同名 4.2 子类中访问父类的成员方法 1. 成员方法名字不同 2. 成员方法名字相同 5. super关键字 6. 子类构…

腾讯云CVM服务器5年可选2核4G和4核8G配置

腾讯云服务器网整理五年云服务器优惠活动 txyfwq.com/go/txy 配置可选2核4G和4核8G&#xff0c;公网带宽可选1M、3M或5M&#xff0c;系统盘为50G高性能云硬盘&#xff0c;标准型S5实例CPU采用主频2.5GHz的Intel Xeon Cascade Lake或者Intel Xeon Cooper Lake处理器&#xff0c;…

市场行情回暖、利好月来袭,Web3 广告业领头羊 Verasity 或迎爆发

随着区块链技术的普及和发展&#xff0c;越来越多的行业正在被区块链技术所重塑&#xff0c;例如金融、游戏行业等&#xff0c;而数字广告行业的结构和运作方式也正在被区块链技术所重塑。 众所周知&#xff0c;传统数字广告行业往往存在着信息不对称、广告欺诈、数字隐私等问题…

工具: PowerShell常用命令

ISE: 打开ISE编辑器 echo: 输出一行信息 mkdir: 创建一个文件夹 mkdir ./MyPlugin文件相关处理 参考&#xff1a; powershell新手向&#xff0c;新建、删除文件及对文件添加内容 参考文档 PowerShell入门教程 语法、环境| Powershell 教程

软件测试基础1:认识软件及测试

功能测试能力:具备对所有软件的功能进行质量验证。 1什么是软件 分类 应用软件系统软件 软件&#xff1a;控制计算机硬件工作的工具。 2软件基本组成 3软件产生过程 4什么是软件测试 软件测试&#xff1a;使用技术手段验证软件是否满足使用需求。 5软件测试目的 减少软件…

智安网络|探索语音识别技术:优势与挑战的全面解析

语音识别技术是人工智能领域的重要应用之一&#xff0c;它通过将语音信号转化为文本&#xff0c;实现了人机交互的一种新形式。随着科技的不断发展&#xff0c;语音识别技术在各个行业中得到了广泛的应用&#xff0c;但同时也存在着一些优势和劣势。 首先&#xff0c;语音识别…

中国首个通过ASIL D认证的IP发布,国产芯片供应商的机会来了

来自智能汽车的“芯”安全需求正在快速爆发。 一方面&#xff0c;随着智能汽车ADAS的快速迭代与逐渐普及化&#xff0c;以及越来越多元化智能座舱功能的快速上车&#xff0c;由此带来的车辆信息安全场景也在与日俱增&#xff0c;例如云端链接、设备身份认证、自动驾驶安全保障…

产品的生命周期

** 没错&#xff0c;产品每次版本选代都是一次重生的机会&#xff0c;每次版本迭代都不亚于一次产品上线首发。 ** 一、产品上线首发 所谓万事开头难&#xff0c;产品上线首发的重要性不言而喻&#xff0c;产品给人的第一印象非常深刻&#xff0c;后期再去扭转非常困难&#…