【无标题】线程学习(18)-多把锁下的线程问题,死锁,活锁,饥饿
多把锁的应用
减小锁粒度,提交并发度。
package com.bo.threadstudy.four;import lombok.extern.slf4j.Slf4j;/*** 多把锁的情况,以及后期的死锁,活锁,饥饿现象,哲学家就餐*/
@Slf4j
public class ManyLockTest {private static final Object lock1 = new Object();private static final Object lock2 = new Object();//使用多把锁,其目的就是将锁的粒度划分的更细,增强并发度,但在嵌套环境下就是死锁public static void main(String[] args) {//假设有一个房间,我现在需要四个人做工,一个房间只能容纳一个人,每个人耗时1秒//这个房子我切割一下,切成两个房子,然后让两四个人分开做工,就提升了并发度,例子不太好,老师的也不咋地,将就把//原本一间方4秒的任务,换成2间两秒for (int i = 0; i < 4; i++) {if(i%2==0){new Thread(() -> {synchronized (lock1){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}log.debug("在做一号工");}},"t1_"+i).start();}else{new Thread(() -> {synchronized (lock2){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}log.debug("在做二号工");}},"t2_"+i).start();}}}}
死锁
满足死锁的条件:
每个资源只能被一个线程所持有。
线程在阻塞等待其它线程持有的资源的时候,自己的资源不会主动释放。
线程持有资源正常运行的情况下,也不会被其它线程的请求释放资源。
多个线程嵌套持有各个线程需要的资源,形成一个闭环。
在这种情况下会出现死锁。写一个简单的死锁例子。
@Slf4j
class DeadLock{private static final Object lock1 = new Object();private static final Object lock2 = new Object();public static void main(String[] args) {new Thread(() -> {while(true){synchronized (lock1){log.debug("t1线程进入lock1");synchronized (lock2){log.debug("t1线程进入lock2");}}}},"t1").start();new Thread(() -> {while(true){synchronized (lock2){log.debug("t2线程进入lock2");synchronized (lock1){log.debug("t2线程进入lock1");}}}},"t2").start();}
}
synchronized确实会造成死锁。那么怎么排查死锁问题。
死锁问题排查
老师讲课过程中,说了两种,第一种:jconolse,第二种,jps查询进程ID使用jsack排查。
这种比较损毁性能,相对而言,正常生产环境采用arthas来进行查询即可。启动arthas-boot.jar后通过thread -b命令查询即可。
哲学家吃饭问题
既然涉及到死锁,那么最经典的问题就是哲学家吃饭问题了,先写一个错误场景。
package com.bo.threadstudy.four;import lombok.extern.slf4j.Slf4j;import java.util.ArrayList;/*** 哲学家吃饭问题*/
public class PhilosopherEatTest {public static void main(String[] args) {Philosopher t1 = new Philosopher("亚里士多德", 1, 2);Philosopher t2 = new Philosopher("柏拉图", 2, 3);Philosopher t3 = new Philosopher("苏格拉底", 3, 4);Philosopher t4 = new Philosopher("但丁", 4, 5);Philosopher t5 = new Philosopher("拿破仑", 5, 1);ArrayList<Philosopher> philosophers = new ArrayList<>();philosophers.add(t1);philosophers.add(t2);philosophers.add(t3);philosophers.add(t4);philosophers.add(t5);for (Philosopher philosopher : philosophers) {new Thread(() -> {philosopher.eat();},philosopher.getName()).start();}}
}/*** 哲学家类*/
@Slf4j
class Philosopher{//姓名private String name;//肯定是需要一双筷子的private Chopsticks chopsticks;public String getName() {return name;}Philosopher(String name, Integer left, Integer right){this.name = name;chopsticks = new Chopsticks();chopsticks.setLeft(left);chopsticks.setRight(right);}//吃饭操作public void eat(){synchronized (chopsticks.getLeft()){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}log.debug(this.name+"拿起了左手的筷子");synchronized(chopsticks.getRight()){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}log.debug(this.name+"拿起了右手的筷子");}}}
}/*** 筷子类*/
class Chopsticks{private Integer left;private Integer right;public Integer getLeft() {return left;}public void setLeft(Integer left) {this.left = left;}public Integer getRight() {return right;}public void setRight(Integer right) {this.right = right;}
}
老师的做法和我大同小异,差距比较大的地,也是筷子我定义成一双,老师定义的一根,不重要。
通过jps配合jstack看到了死锁现象。这里的问题,就是线程各自持有各自的资源无法释放。
那么,这个问题,解决,只需要它们有序执行就可以了。在创建对象时,控制下锁的获取顺序。
这种方案当然可以,但这样性能比较低,因为假如t1-t4一块拿起筷子,得等t4吃完后,t3才能吃,接下来就是t2,t1。最终是t5,是按顺序执行的。
在这里共耗费了6秒。
解决方法,改造一下eat()方法。
//吃饭操作public void eat(){synchronized (chopsticks.getLeft()){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if(chopsticks.getLeft()%2 == 0){//左手边筷子是偶数号的,先放下,等其他人吃完try {chopsticks.getLeft().wait();} catch (InterruptedException e) {e.printStackTrace();}}log.debug(this.name+"拿起了左手的筷子");synchronized(chopsticks.getRight()){try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}log.debug(this.name+"拿起了右手的筷子");//吃完后唤醒(先吃完的这批,左手号筷子是偶数的,它们右手的筷子是奇数,唤醒也该唤醒左手的筷子)chopsticks.getRight().notifyAll();}}}
这里耗费了3秒。没有什么问题。性能确实有所提升。但再换个场景,类似问题我能快速解决吗,也不确定啊?还是不够强。
后续会用ReetrantLock中的tryLock()方法再试试。
活锁
多个线程之间,在互相改变对方的执行条件,导致谁都没有办法结束。这里的话用双重校验锁保证了下线程安全。
package com.bo.threadstudy.four;import lombok.extern.slf4j.Slf4j;/*** 活锁测试样例*/
@Slf4j
public class AliveLockTest {private static Integer count = 1000;private static Object lock = new Object();public static void main(String[] args) {new Thread(() -> {while(count > 0){synchronized (lock){//双重校验,保证线程安全if(count>0){try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}//synchronized应该是保证结果写到主存里了count--;log.debug("t1线程count递减值"+count);}}}},"t1").start();new Thread(() -> {while(count < 2000){synchronized (lock){if(count<2000){try {Thread.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}count++;log.debug("t2线程count递加值"+count);}}}},"t2").start();}}
饥饿
线程饥饿,很多教程中把饥饿定义为,一个线程由于优先级太低,始终得不到 CPU 调度执行,也不能够结束,饥饿的情况不易演示,讲读写锁时会涉及饥饿问题。
虽然老师也讲了样例,就是多个线程执行,有的线程频繁获取锁资源,有的线程就是获取不到锁资源。这个的话,后期ReadWriteLock会讲到,先过吧。也明白是什么情况。
不行,这些东西,我得找点业务场景练练。得实操。
【无标题】线程学习(18)-多把锁下的线程问题,死锁,活锁,饥饿相关推荐
- 线程基础知识_Synchronized_ThreadAPI_自定义锁_获取线程运行时异常
Synchronized synchronized包含monitor enter, monitor exit 2个JVM指令(遵循happens-before原则), 执行monitor exit之前 ...
- 【无标题】学习笔记-2022.8.1-8.6
文献阅读笔记5 一.文章信息 1. 作者 Berta Bescos, Jose M. F ´ acil, Javier Civera and Jos ´ e Neira 2.单位 University ...
- 【无标题】学习CADD-AMBER--薛定谔--代谢组学及网络药理学知识内容
CADD专题 CADD-同源建模-蛋白-薛定谔-Amber-代谢组学及网络药理学专题学习 生物分子互作基础 1.生物分子互作用研究方法 1.1蛋白-小分子.蛋白-蛋白相互作用原理 1.2 分子对接研究 ...
- Java线程学习实例——采用同步锁,互斥锁与同步锁的区别,synchronized的使用方法
栗子来源:https://blog.csdn.net/wenzhi20102321/article/details/52524545 首先对java中同步锁与互斥锁进行区分,主要来源于知乎中的大佬总结 ...
- 【无标题】学习贪吃蛇代码
学习贪吃蛇代码
- 【无标题】学习kotlin语言
//: 注饰 int: 整型 long: 长整型 short: 短整型 float: ...
- 嵌入式 linux 进程锁,嵌入式 Linux线程锁详解pthread_mutexattr_t
在Posix Thread中定义有一套专门用于线程同步的mutex函数. 1. 创建和销毁 有两种方法创建互斥锁,静态方式和动态方式.POSIX定义了一个宏PTHREAD_MUTEX_INITIALI ...
- java同步锁优化方案学习笔记(偏向锁,轻量级锁,自旋锁,重量级锁)
目录 一,概述 二,CAS算法 三,Java对象的对象头,以及Mark Word 四,偏向锁 Baised Lock 五,轻量级锁 六,自旋锁 SpinLock 七,重量级锁 八,在应用层提高锁效率的 ...
- LINUX线程同步:原子操作、锁、二元信号量、信号量、互斥量、临界区、读写锁、条件变量等
注:摘自<程序员的自我修养>相关章节. 原子操作 共享数据(全局变量或堆变量)的自增(++)操作在多线程环境下会出现错误是因为这个操作(一条c语句)被编译为汇编代码后不止一条指令,因此在执 ...
最新文章
- 新手推荐!天池数据挖掘挑战赛,2019全球数据智能大赛正式启动!60万奖金等你来拿...
- 孪生素数 java代码_科学网—孪生素数猜想——利用 Java + 正则表达式 输出孪生素数对 - 马廷灿的博文...
- nova-scheduler详解 openstack-ice版
- Mysql无法创建外键的原因
- HBase、Redis、MongoDB、Couchbase、LevelDB 五款主流NoSQL数据库大比拼
- Python 空字符串转化问题:ValueError: invalid literal for int() with base 10: ' ',原因及解决方法。
- git仓库迁移和更新远程仓库地址
- python自动化开发_python自动化开发-2
- APP支付报错ALI40247解决方案
- 4.2路由算法与路由协议概述
- poi生成word不可以修改_操作不懂技术就可以做小程序无限生成平台的创业项目实操教程...
- java 建立ssh隧道_如何使用IntelliJ和JDBC SSH隧道并连接到数据库?
- [Usaco2006 Open]County Fair Events 参加节日庆祝
- JAVA加载 编译 运行,在Java 7中编译的加载/运行类6
- 浅谈欧几里得算法求最大公约数(GCD)的原理及简单应用
- python计算moran_空间自相关 (Global Moran's I)
- 知道如何防止域名被封,干货!赶紧收藏
- 机器学习基石(林軒田)笔记之十二
- 10款最佳免费WiFi黑客工具(附传送门)
- mysql 获取农历年份_php下实现农历日历的代码