【Java】死锁问题及ThreadLocal

news/2024/12/29 13:49:31/

  • 什么是死锁
  • 分析过程
  • 发生死锁的原因
  • 避免死锁
  • ThreadLocal

什么是死锁

多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不可能正常终止。这是一个最严重的BUG之一

分析过程

1.一个线程一把锁
一个线程对一个锁加锁两次,如果是可重入锁,不会产生死锁,如果是不可重入锁也加不了两次,谈不上死锁。

2.两个线程两把锁
车钥匙锁家里,家里的钥匙锁车里。

复现这个现象

public class Demo05_DeadLock {public static void main(String[] args) {// 定义两个锁对象Object locker1 = new Object();Object locker2 = new Object();// 线程1,先获取locker1 再获取 locker2Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + " t1 申请locker1..");synchronized (locker1) {System.out.println(Thread.currentThread().getName() + " t1 获取到了locker1.");// 模拟业务处理过程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 获取locker2System.out.println(Thread.currentThread().getName() + " t1 申请locker2...");synchronized (locker2) {System.out.println(Thread.currentThread().getName() + " t1 获取到了两把锁资源");}}});// 启动t1t1.start();// 线程2,先获取locker2 再获取 locker1Thread t2 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + " t2 申请locker2..");synchronized (locker2) {System.out.println(Thread.currentThread().getName() + " t2 获取到了locker2.");// 模拟业务处理过程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 获取locker1System.out.println(Thread.currentThread().getName() + " t2 申请locker1...");synchronized (locker1) {System.out.println(Thread.currentThread().getName() + " t2 获取到了两把锁资源");}}});// 启动t2t2.start();}
}

在这里插入图片描述

发生死锁的原因

1.互斥使用:锁A被线程1占用了,线程2就不能用了;
2.不可抢占:锁A被线程1占用了,线程2不能主动把锁A抢过来,除非线程1主动释放;
3.请求保持:有多把锁,线程1拿到锁A之后不释放,还要继续再拿;
4.循环等待:线程1等待线程2释放锁,线程2要释放锁得等待线程3先释放锁,线程3释放锁得等待线程1释放锁,形成了循环关系。

🔴有这样一个经典的“哲学家吃面问题”
有个桌子, 围着一圈哲学家, 桌子中间放着一盘意大利面. 每个哲学家两两之间,放着一根筷子,每个哲学家只做两件事: 思考人生或者吃面条。思考人生的时候就会放下筷子。吃面条就会拿起左右两边的筷子(先拿起左边,再拿起右边)。如果哲学学家发现筷子拿不起来了(被别人占用了), 就会阻塞等待。
假设同一时刻, 五个哲学家同时拿起左手边的筷子,然后再尝试拿右手的筷子, 就会发现右手的筷子都被占用了。由于哲 学家们互不相让, 这个时候就形成了死锁
在这里插入图片描述

避免死锁

以上四条是死锁产生的必要条件。在死锁的情况下如果打破上述任何一个条件,便可让死锁消失。
1.互斥使用:这个不能被打破,这是锁的基本特性;
2.不可抢占:这个也不能被打破,这也是锁的基本特性;
3.请求保持:这个可以被打破,取决于代码怎么写;
4.循环等待:约定好加锁顺序就可以打破循环等待。

👀以哲学家就餐问题为例,不像以前那样先拿左手的筷子再拿右手的筷子,重新安排一下拿筷子的顺序。
1.让每个哲学家必须先去拿编号小的筷子;
2.拿到后再去拿编号大的筷子;
3.如果五个哲学家同时去拿筷子的话那么5和1都会先抢筷子1;
4.筷子1只有一个哲学家会先拿到,抢不到的那个哲学家就要等;
5.哲学家4就会获得到两只筷子,吃完面后,其他的可以获取的筷子的哲学家继续吃;
6.最终其他哲学家吃完之后,5号再获取到筷子完成吃面。
在这里插入图片描述

在复现的例子中,t1.locker1——>locker2,t2.locker2——>locker1,调整加锁顺序,就可以避免循环等待。

public class Demo05_DeadLock {public static void main(String[] args) {// 定义两个锁对象Object locker1 = new Object();Object locker2 = new Object();// 线程1,先获取locker1 再获取 locker2Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + " t1 申请locker1..");synchronized (locker1) {System.out.println(Thread.currentThread().getName() + " t1 获取到了locker1.");// 模拟业务处理过程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 获取locker2System.out.println(Thread.currentThread().getName() + " t1 申请locker2...");synchronized (locker2) {System.out.println(Thread.currentThread().getName() + " t1 获取到了两把锁资源");}}});// 启动t1t1.start();// 线程2,先获取locker2 再获取 locker1Thread t2 = new Thread(() -> {System.out.println(Thread.currentThread().getName() + " t2 申请locker1..");synchronized (locker1) {System.out.println(Thread.currentThread().getName() + " t2 获取到了locker1.");// 模拟业务处理过程try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}// 获取locker1System.out.println(Thread.currentThread().getName() + " t2 申请locker2...");synchronized (locker2) {System.out.println(Thread.currentThread().getName() + " t2 获取到了两把锁资源");}}});// 启动t2t2.start();}
}

ThreadLocal

有这样一个场景,多个班级,每个班级要统计一下班里人数,根据人数去做校服。这时可以使用ThreadLocal,不是让所有线程修改同一个共享变量,而是让每个线程修改各自的变量。

public class Demo06_ThreadLocal {// 初始化一个ThreadLocalprivate static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();public static void main(String[] args) {// 多个线程分别去统计人数Thread thread1 = new Thread(() -> {// 统计人数int count = 35;threadLocal.set(count);
//            Integer value = threadLocal.get();
//            System.out.println(value);// 订制校服print();}, "threadNameClass1");Thread thread2 = new Thread(() -> {// 统计人数int count = 40;threadLocal.set(count);
//            Integer value = threadLocal.get();
//            System.out.println(value);// 订制校服print();}, "threadNameClass2");thread1.start();thread2.start();}// 订制校服public static void print() {// 从threadLocal中获取值Integer value = threadLocal.get();System.out.println(Thread.currentThread().getName() + " : 需要订制 " + value + "套校服.");}
}

set()方法:
在这里插入图片描述

在这里插入图片描述


继续加油~
在这里插入图片描述


http://www.ppmy.cn/news/489308.html

相关文章

特定偏好的效用函数——CES效用函数

说明&#xff1a;做单调变化(monotonic transformation)是合理的&#xff0c;因为效用的数值大小是没有意义的&#xff0c;有意义的是相对排序&#xff0c;体现的是序数性质而不是基数性质 见p51~p52 中文第11版教材上关于CES效用函数的形式是如下的表述&#xff1a; U ( x ,…

CES生产函数中参数的意义

CES生产函数是形如的函数&#xff0c;其中是各投入物对产出的贡献&#xff0c;是弹性参数&#xff0c;是替代弹性 至于为什么是替代弹性&#xff0c;可参见这篇博文&#xff08;注意ta的公式中的和我的差一个正负号&#xff09; https://blog.csdn.net/baimafujinji/article/…

2017 CES

2017 CES 小结 本次参展是逆序&#xff0c;这不是个好主意&#xff0c;后面到了大的展馆都没有力气了。 小结描述个人觉得技术值得关注的部分&#xff1a; 1. Dream World 网址&#xff1a;http://www.dreamworldvision.com/ 这家公司的DreamGlass特别好&#xff0c;体验了…

2018年CES展总结

CES&#xff08;International Consumer Electronic Show&#xff09;展是全球消费类电子最新产品秀&#xff0c;有助于相关企业打响国际知名度。CES的展出品预示着电子类消费产品的发展方向。现将王煜全的介绍以及网络中的一些资料总结如下&#xff1a; 1. 中国企业在全球影响…

CES2019前瞻:影音技术如何改变我们的生活

广受关注的国际消费类电子产品展览会CES 2019即将于美国时间1月8日在赌城拉斯维加斯拉开帷幕。每年的CES既是各大厂商们秀肌肉的舞台&#xff0c;也是我们这些普通消费者了解最新科技产品动态的重要渠道。今年的CES参展人数达到了18万&#xff0c;参展公司超过4000家&#xff0…

阿里云服务器ces

允许公网通过 HTTP、HTTPS 等服务访问实例 https://help.aliyun.com/document_detail/25475.html?spm5176.2020520101.0.0.3ca96b0b3KGTPq#allowHttp

看了CES之后,跳槽到朝阳行业有着落了

简介 国际消费类电子产品展览会&#xff08;International Consumer Electronics Show&#xff0c;简称CES&#xff09;&#xff0c;由美国电子消费品制造商协会&#xff08;简称CTA&#xff09;主办&#xff0c;旨在促进尖端电子技术和现代生活的紧密结合。 该展始于1967年&…