JUC并发编程——多把锁
创始人
2024-05-30 01:50:13
0

一、多八锁

多把不相干的锁
一间大屋子有两个功能:睡觉、学习,互不相干。

现在小南要学习,小女要睡觉,但如果只用一间屋子(一个对象锁)的话,那么并发度很低

解决方法就是准备多个房间(对个对象锁)

例如

import cn.itcast.n2.util.Sleeper;
import lombok.extern.slf4j.Slf4j;public class TestMultiLock {public static void main(String[] args) {BigRoom bigRoom = new BigRoom();new Thread(() -> {bigRoom.study();},"小南").start();new Thread(() -> {bigRoom.sleep();},"小女").start();}
}@Slf4j(topic = "c.BigRoom")
class BigRoom {public void sleep() {synchronized (this) {log.debug("sleeping 2 小时");Sleeper.sleep(2);}}public void study() {synchronized (this) {log.debug("study 1 小时");Sleeper.sleep(1);}}
}

运行结果:(并发度较低)
在这里插入图片描述

改进:(加入多把锁)

import cn.itcast.n2.util.Sleeper;
import lombok.extern.slf4j.Slf4j;public class TestMultiLock {public static void main(String[] args) {BigRoom bigRoom = new BigRoom();new Thread(() -> {bigRoom.study();},"小南").start();new Thread(() -> {bigRoom.sleep();},"小女").start();}
}@Slf4j(topic = "c.BigRoom")
class BigRoom {private final Object studyRoom = new Object();private final Object bedRoom = new Object();public void sleep() {synchronized (bedRoom) {log.debug("sleeping 2 小时");Sleeper.sleep(2);}}public void study() {synchronized (studyRoom) {log.debug("study 1 小时");Sleeper.sleep(1);}}
}

改进后运行结果:用多八把锁/细粒度的锁提高程序的并发性需保证业务间无关联
在这里插入图片描述

将锁的粒度细分

class BigRoom {//额外创建对象来作为锁private final Object studyRoom = new Object();private final Object bedRoom = new Object();
}

● 好处:可以增强并发度
● 坏处:如果一个线程需要同时获取多把锁,就容易发生死锁

二、活跃性

2.1 死锁

有这样的情况:一个线程需要同时获取多把锁,这时就容易发生死锁

t1 线程 获得 A对象 锁,接下来想获取 B对象 的锁
t2 线程 获得 B对象 锁,接下来想获取 A对象 的锁

例:各自均有一把锁,都想获取对方锁时会产生死锁,代码最终无法得到执行

import lombok.extern.slf4j.Slf4j;import static cn.itcast.n2.util.Sleeper.sleep;@Slf4j(topic = "c.TestDeadLock")
public class TestDeadLock {public static void main(String[] args) {test1();}private static void test1() {Object A = new Object();Object B = new Object();// t1线程获取A对象上的锁Thread t1 = new Thread(() -> {synchronized (A) {log.debug("lock A");sleep(1);// t1线程经1s后尝试获取锁Bsynchronized (B) {log.debug("lock B");log.debug("操作...");}}}, "t1");// t2线程获取B对象上的锁Thread t2 = new Thread(() -> {synchronized (B) {log.debug("lock B");sleep(0.5);// t2线程经0.5s后尝试获取锁Asynchronized (A) {log.debug("lock A");log.debug("操作...");}}}, "t2");t1.start();t2.start();}
}

2.2 定位死锁

死锁在多线程是较常见的。检测死锁可以使用 jconsole工具,或者使用 jps 定位进程 id,再用 jstack 定位死锁:

:使用 jps 定位进程 id,再用 jstack 定位
在这里插入图片描述

打印死锁信息
在这里插入图片描述

:使用jconsole工具

运行jconsole
在这里插入图片描述

在这里插入图片描述

监测出出现错误的代码行数
在这里插入图片描述
【活跃性】问题,除了死锁以外,还有活锁和饥饿者两种情

2.3 哲学家就餐问题

在这里插入图片描述
有五位哲学家,围坐在圆桌旁。

● 他们只做两件事,思考和吃饭,思考一会吃口饭,吃完饭后接着思考。
● 吃饭时要用两根筷子吃,桌上共有 5 根筷子,每位哲学家左右手边各有一根筷子。
● 如果筷子被身边的人拿着,自己就得等待

测试结果:
正常就餐几轮后不能再继续就餐出现无限等待(每个哲学家各持有一根筷子,等待对方放下筷子)

2.4 活锁

活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束。

例如

import lombok.extern.slf4j.Slf4j;
import static cn.itcast.n2.util.Sleeper.sleep;@Slf4j(topic = "c.TestLiveLock")
public class TestLiveLock {static volatile int count = 10;static final Object lock = new Object();public static void main(String[] args) {new Thread(() -> {// 期望减到 0 退出循环while (count > 0) {sleep(0.2);count--;log.debug("count: {}", count);}}, "t1").start();new Thread(() -> {// 期望超过 20 退出循环while (count < 20) {sleep(0.2);count++;log.debug("count: {}", count);}}, "t2").start();}
}

运行结果:(不断在10左右进行加加减减操作,线程失踪无法停止)
在这里插入图片描述

死锁与活锁的区别:
死锁是两个线程持有对方需要的锁,导致线程都无法继续向下运行,两个线程均陷入阻塞;而活锁两个线程均未阻塞,都在使用CPU不断运行,但由于改变对方的结束条件导致两个线程都无法结束

解决活锁的办法
● 使两个线程的执行时间有一定的交错(不集中在一起执行/设置睡眠的时间为一个随机数===>将其指令交错开,第一个线程很快运行完,第二个线程将没有机会改变对方的结束条件)

在开发中遇到活锁的情况,可增加随机睡眠时间来避免活锁的产生

2.5 饥饿

很多教程中将饥饿定义为,一个线程由于优先级太低,始终得不到 CPU 调度执行,也不能够结束,饥饿的情况不易演示,讲读写锁时会涉及饥饿问题

观察一个线程饥饿的例子,可以先观察使用顺序加锁的方式解决之前的死锁问题
在这里插入图片描述

相同顺序加锁的解决方案
在这里插入图片描述

线程1按AB的顺序加锁,先获得锁A,线程2也是按AB的顺序进行加锁,线程2此时想获取对象A的锁时获取失败(进入对象A的EntryList中阻塞),这时线程1再尝试获取B对象的锁。这样线程1均可以将AB两个锁获取到。等其释放完后线程2也按相同的顺序获取AB对象。

相关内容

热门资讯

AWSECS:访问外部网络时出... 如果您在AWS ECS中部署了应用程序,并且该应用程序需要访问外部网络,但是无法正常访问,可能是因为...
AWSElasticBeans... 在Dockerfile中手动配置nginx反向代理。例如,在Dockerfile中添加以下代码:FR...
AWR报告解读 WORKLOAD REPOSITORY PDB report (PDB snapshots) AW...
AWS管理控制台菜单和权限 要在AWS管理控制台中创建菜单和权限,您可以使用AWS Identity and Access Ma...
北信源内网安全管理卸载 北信源内网安全管理是一款网络安全管理软件,主要用于保护内网安全。在日常使用过程中,卸载该软件是一种常...
​ToDesk 远程工具安装及... 目录 前言 ToDesk 优势 ToDesk 下载安装 ToDesk 功能展示 文件传输 设备链接 ...
Azure构建流程(Power... 这可能是由于配置错误导致的问题。请检查构建流程任务中的“发布构建制品”步骤,确保正确配置了“Arti...
群晖外网访问终极解决方法:IP... 写在前面的话 受够了群晖的quickconnet的小水管了,急需一个新的解决方法&#x...
AWSECS:哪种网络模式具有... 使用AWS ECS中的awsvpc网络模式来获得最佳性能。awsvpc网络模式允许ECS任务直接在V...
不能访问光猫的的管理页面 光猫是现代家庭宽带网络的重要组成部分,它可以提供高速稳定的网络连接。但是,有时候我们会遇到不能访问光...