多把锁的应用

减小锁粒度,提交并发度。

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)-多把锁下的线程问题,死锁,活锁,饥饿相关推荐

  1. 线程基础知识_Synchronized_ThreadAPI_自定义锁_获取线程运行时异常

    Synchronized synchronized包含monitor enter, monitor exit 2个JVM指令(遵循happens-before原则), 执行monitor exit之前 ...

  2. 【无标题】学习笔记-2022.8.1-8.6

    文献阅读笔记5 一.文章信息 1. 作者 Berta Bescos, Jose M. F ´ acil, Javier Civera and Jos ´ e Neira 2.单位 University ...

  3. 【无标题】学习CADD-AMBER--薛定谔--代谢组学及网络药理学知识内容

    CADD专题 CADD-同源建模-蛋白-薛定谔-Amber-代谢组学及网络药理学专题学习 生物分子互作基础 1.生物分子互作用研究方法 1.1蛋白-小分子.蛋白-蛋白相互作用原理 1.2 分子对接研究 ...

  4. Java线程学习实例——采用同步锁,互斥锁与同步锁的区别,synchronized的使用方法

    栗子来源:https://blog.csdn.net/wenzhi20102321/article/details/52524545 首先对java中同步锁与互斥锁进行区分,主要来源于知乎中的大佬总结 ...

  5. 【无标题】学习贪吃蛇代码

    学习贪吃蛇代码

  6. 【无标题】学习kotlin语言

    //: 注饰                                int: 整型 long: 长整型                           short: 短整型 float: ...

  7. 嵌入式 linux 进程锁,嵌入式  Linux线程锁详解pthread_mutexattr_t

    在Posix Thread中定义有一套专门用于线程同步的mutex函数. 1. 创建和销毁 有两种方法创建互斥锁,静态方式和动态方式.POSIX定义了一个宏PTHREAD_MUTEX_INITIALI ...

  8. java同步锁优化方案学习笔记(偏向锁,轻量级锁,自旋锁,重量级锁)

    目录 一,概述 二,CAS算法 三,Java对象的对象头,以及Mark Word 四,偏向锁 Baised Lock 五,轻量级锁 六,自旋锁 SpinLock 七,重量级锁 八,在应用层提高锁效率的 ...

  9. LINUX线程同步:原子操作、锁、二元信号量、信号量、互斥量、临界区、读写锁、条件变量等

    注:摘自<程序员的自我修养>相关章节. 原子操作 共享数据(全局变量或堆变量)的自增(++)操作在多线程环境下会出现错误是因为这个操作(一条c语句)被编译为汇编代码后不止一条指令,因此在执 ...

最新文章

  1. 新手推荐!天池数据挖掘挑战赛,2019全球数据智能大赛正式启动!60万奖金等你来拿...
  2. 孪生素数 java代码_科学网—孪生素数猜想——利用 Java + 正则表达式 输出孪生素数对 - 马廷灿的博文...
  3. nova-scheduler详解 openstack-ice版
  4. Mysql无法创建外键的原因
  5. HBase、Redis、MongoDB、Couchbase、LevelDB 五款主流NoSQL数据库大比拼
  6. Python 空字符串转化问题:ValueError: invalid literal for int() with base 10: ' ',原因及解决方法。
  7. git仓库迁移和更新远程仓库地址
  8. python自动化开发_python自动化开发-2
  9. APP支付报错ALI40247解决方案
  10. 4.2路由算法与路由协议概述
  11. poi生成word不可以修改_操作不懂技术就可以做小程序无限生成平台的创业项目实操教程...
  12. java 建立ssh隧道_如何使用IntelliJ和JDBC SSH隧道并连接到数据库?
  13. [Usaco2006 Open]County Fair Events 参加节日庆祝
  14. JAVA加载 编译 运行,在Java 7中编译的加载/运行类6
  15. 浅谈欧几里得算法求最大公约数(GCD)的原理及简单应用
  16. python计算moran_空间自相关 (Global Moran's I)
  17. 知道如何防止域名被封,干货!赶紧收藏
  18. 机器学习基石(林軒田)笔记之十二
  19. 10款最佳免费WiFi黑客工具(附传送门)
  20. mysql 获取农历年份_php下实现农历日历的代码

热门文章

  1. Liferay 6.0 R2 run log at first time
  2. 辨析-06-基础日语中格助词的常见固定用法
  3. YYWebImage的基本用法
  4. 关于华为蓝牙耳机连不上计算机的问题
  5. codeforce div1+2 621 C.cow message
  6. 在linux中tldr的用法,快速查看linux命令的用法----------TLDR
  7. C语言小白上楼梯问题(递归)
  8. 会议如何进行网络直播
  9. triangulatePoints函数
  10. 【秋招基础知识】【3】机器学习常见判别模型和生成模型