网站每天1万ip能收入多少,网站的整体风格包括,wordpress获取路径,推广普通话的宣传语1.死锁是什么#xff1f;
死锁一定发生在并发环境中#xff0c;死锁是一种状态#xff0c;当两个(或者多个线程)相互持有对方所需要的资源#xff0c;却又都不主动释放手中持有的资源#xff0c;导致大家都获取不到自己想要的资源#xff0c;所有相关的线程无法继续执行…1.死锁是什么
死锁一定发生在并发环境中死锁是一种状态当两个(或者多个线程)相互持有对方所需要的资源却又都不主动释放手中持有的资源导致大家都获取不到自己想要的资源所有相关的线程无法继续执行。
2.死锁案例
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;/*** author weijie* date 2020/4/28 10:43*/
public class DeadLockDemo {Object o1 new Object();Object o2 new Object();class Task1 implements Runnable{Overridepublic void run() {synchronized (o1){System.out.println(task1 start ...);String threadName Thread.currentThread().getName();System.out.print(threadName 获取i1对象锁---);/*** 阻塞当前线程*/try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}synchronized (o2){System.out.println(获取i2对象锁);}System.out.println(task2 end ...);}}}class Task2 implements Runnable{Overridepublic void run() {synchronized (o2) {System.out.println(task2 start ...);String threadName Thread.currentThread().getName();System.out.print(threadName 获取i2对象锁---);synchronized (o1) {System.out.println(获取i1对象锁);}System.out.println(task 2 end ...);}}}public static void main(String[] args) {DeadLockDemo deadLockDemo new DeadLockDemo();ExecutorService executorService Executors.newFixedThreadPool(2);executorService.submit(deadLockDemo.new Task1());executorService.submit(deadLockDemo.new Task2());}}
3.死锁必须满足的四个条件
互斥条件每个资源同时只能被一个线程使用。线程A占用了线程B一定不能使用必须等线程A释放资源后线程B才能继续使用。请求与保持条件也就是说当线程A占用资源时由于线程A有其他业务逻辑导致线程阻塞等此时不能自动释放资源如果自动释放资源那就不会发生死锁问题。不剥夺条件当线程A占用资源时不能被线程B剥夺、抢占资源。
4.如何用命令行和代码定位死锁
1.通过命令行定位
通过jps指令获取服务进程id
56402 MustDeadLock
56403 Launcher
56474 Jps
55051 KotlinCompileDaemon
jstack pid指令来打印信息会出现“Found one Java-level deadlock”表示死锁接着我们可以通过打印信息定位死锁问题
Found one Java-level deadlock:t2:waiting to lock monitor 0x00007fa06c004a18 (object 0x000000076adabaf0, a java.lang.Object),which is held by t1
t1:waiting to lock monitor 0x00007fa06c007358 (object 0x000000076adabb00, a java.lang.Object),which is held by t2Java stack information for the threads listed above:t2:at lesson67.MustDeadLock.run(MustDeadLock.java:31)- waiting to lock 0x000000076adabaf0 (a java.lang.Object)- locked 0x000000076adabb00 (a java.lang.Object)at java.lang.Thread.run(Thread.java:748)
t1:at lesson67.MustDeadLock.run(MustDeadLock.java:19)- waiting to lock 0x000000076adabb00 (a java.lang.Object)- locked 0x000000076adabaf0 (a java.lang.Object)at java.lang.Thread.run(Thread.java:748)Found 1 deadlock
在这里它首先会打印“Found one Java-level deadlock”表明“找到了一个死锁”。 然后是更详细的信息从中间这部分的信息中可以看出 t2 线程想要去获取这个尾号为 af0 的锁对象但是它被 t1 线程持有同时 t2 持有尾号为 b00 的锁对象 相反t1 想要获取尾号为 b00 的锁对象但是它被 t2 线程持有同时 t1 持有的却是尾号为 af0 的锁对象 这就形成了一个依赖环路发生了死锁。 最后它还打印出了“Found 1 deadlock.”
可以看出jstack 工具不但帮我们找到了死锁甚至还把哪个线程、想要获取哪个锁、形成什么样的环路都告诉我们了当我们有了这样的信息之后死锁就非常容易定位了所以接下来我们就可以进一步修改代码来避免死锁了。
2.代码定位死锁
ThreadMXBean用来定位死锁问题
在main方法中添加如下代码 //保证发生死锁Thread.sleep(3000);System.out.println(定位死锁信息);ThreadMXBean threadMXBean ManagementFactory.getThreadMXBean();long[] deadlockedThreads threadMXBean.findDeadlockedThreads();if(deadlockedThreads ! null deadlockedThreads.length 0){for (int i 0; i deadlockedThreads.length; i){long deadlockedThread deadlockedThreads[i];ThreadInfo threadInfo threadMXBean.getThreadInfo(deadlockedThread);System.out.println(线程id为threadInfo.getThreadId(),线程名为 threadInfo.getThreadName()的线程已经发生死锁需要的锁正被线程threadInfo.getLockOwnerName()持有。);}}可以看出ThreadMXBean 也可以帮我们找到并定位死锁如果我们在业务代码中加入这样的检测那我们就可以在发生死锁的时候及时地定位同时进行报警等其他处理也就增强了我们程序的健壮性。
5.经典的哲学家问题
1.问题描述
哲学家就餐问题也被称为刀叉问题或者吃面问题。我们先来描述一下这个问题所要说明的事情这个问题如下图所示 有 5 个哲学家他们面前都有一双筷子即左手有一根筷子右手有一根筷子。当然这个问题有多个版本的描述可以说是筷子也可以说是一刀一叉因为吃牛排的时候需要刀和叉缺一不可也有说是用两把叉子来吃意大利面。这里具体是刀叉还是筷子并不重要重要的是必须要同时持有左右两边的两个才行也就是说哲学家左手要拿到一根筷子右手也要拿到一根筷子在这种情况下哲学家才能吃饭。为了方便理解我们选取和我国传统最贴近的筷子来说明这个问题。
为什么选择哲学家呢因为哲学家的特点是喜欢思考所以我们可以把哲学家一天的行为抽象为思考然后吃饭并且他们吃饭的时候要用一双筷子而不能只用一根筷子。
1. 主流程
我们来看一下哲学家就餐的主流程。哲学家如果想吃饭他会先尝试拿起左手的筷子然后再尝试拿起右手的筷子如果某一根筷子被别人使用了他就得等待他人用完用完之后他人自然会把筷子放回原位接着他把筷子拿起来就可以吃了不考虑卫生问题。这就是哲学家就餐的最主要流程。
2. 流程的伪代码
我们来看一下这个流程的伪代码如下所示
while(true) { // 思考人生、宇宙、万物...think();// 思考后感到饿了需要拿筷子开始吃饭pick_up_left_chopstick();pick_up_right_chopstick();eat();put_down_right_chopstick();put_down_left_chopstick();// 吃完饭后继续思考人生、宇宙、万物...
}
while(true) 代表整个是一个无限循环。在每个循环中哲学家首先会开始思考思考一段时间之后这个时间长度可以是随机的他感到饿了就准备开始吃饭。在吃饭之前必须先拿到左手的筷子再拿到右手的筷子然后才开始吃饭吃完之后先放回右手的筷子再放回左手的筷子由于这是个 while 循环所以他就会继续思考人生开启下一个循环。这就是整个过程。
3.有死锁和资源耗尽的风险
这里存在什么风险呢就是发生死锁的风险。如下面的动画所示 根据我们的逻辑规定在拿起左手边的筷子之后下一步是去拿右手的筷子。大部分情况下右边的哲学家正在思考所以当前哲学家的右手边的筷子是空闲的或者如果右边的哲学家正在吃饭那么当前的哲学家就等右边的哲学家吃完饭并释放筷子于是当前哲学家就能拿到了他右手边的筷子了。
但是如果每个哲学家都同时拿起左手的筷子那么就形成了环形依赖在这种特殊的情况下每个人都拿着左手的筷子都缺少右手的筷子那么就没有人可以开始吃饭了自然也就没有人会放下手中的筷子。这就陷入了死锁形成了一个相互等待的情况。
4.代码演示
代码如下所示
package com.netty.rpc.test.util;import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;public class DiningPhilosophers {public static class Philosopher implements Runnable {private Object leftChopstick;private Object rightChopstick;public Philosopher(Object leftChopstick, Object rightChopstick) {this.leftChopstick leftChopstick;this.rightChopstick rightChopstick;}Overridepublic void run() {try {while (true) {doAction(思考人生、宇宙、万物、灵魂...);synchronized (leftChopstick) {doAction(拿起左边的筷子);synchronized (rightChopstick) {doAction(拿起右边的筷子);doAction(吃饭);doAction(放下右边的筷子);}doAction(放下左边的筷子);}}} catch (InterruptedException e) {e.printStackTrace();}}private void doAction(String action) throws InterruptedException {System.out.println(Thread.currentThread().getName() action);Thread.sleep((long) (Math.random() * 10));}}public static void main(String[] args) throws InterruptedException {Philosopher[] philosophers new Philosopher[5];Object[] chopsticks new Object[philosophers.length];for (int i 0; i chopsticks.length; i) {chopsticks[i] new Object();}for (int i 0; i philosophers.length; i) {Object leftChopstick chopsticks[i];Object rightChopstick chopsticks[(i 1) % chopsticks.length];philosophers[i] new Philosopher(rightChopstick, leftChopstick);new Thread(philosophers[i], 哲学家 (i 1) 号).start();}Thread.sleep(1000 * 10);ThreadMXBean threadMXBean ManagementFactory.getThreadMXBean();long[] deadlockedThreads threadMXBean.findDeadlockedThreads();if(deadlockedThreads ! null deadlockedThreads.length 0){for (int i 0; i deadlockedThreads.length; i){long deadlockedThread deadlockedThreads[i];ThreadInfo threadInfo threadMXBean.getThreadInfo(deadlockedThread);System.out.println(线程id为threadInfo.getThreadId(),线程名为 threadInfo.getThreadName()的线程已经发生死锁需要的锁正被线程threadInfo.getLockOwnerName()持有。);}}}
}
运行后截图 哲学家 1、3、2、4、5 几乎同时开始思考然后假设他们思考的时间比较相近于是他们都在几乎同一时刻想开始吃饭都纷纷拿起左手的筷子这时就陷入了死锁状态没有人可以拿到右手的筷子也就没有人可以吃饭于是陷入了无穷等待这就是经典的哲学家就餐问题。
5.多种解决方案
对于这个问题我们该如何解决呢有多种解决方案这里我们讲讲其中的几种。前面我们讲过要想解决死锁问题只要破坏死锁四个必要条件的任何一个都可以。
1. 服务员检查
第一个解决方案就是引入服务员检查机制。比如我们引入一个服务员当每次哲学家要吃饭时他需要先询问服务员我现在能否去拿筷子吃饭此时服务员先判断他拿筷子有没有发生死锁的可能假如有的话服务员会说现在不允许你吃饭。这是一种解决方案。
2. 领导调节
我们根据上一讲的死锁检测和恢复策略可以引入一个领导这个领导进行定期巡视。如果他发现已经发生死锁了就会剥夺某一个哲学家的筷子让他放下。这样一来由于这个人的牺牲其他的哲学家就都可以吃饭了。这也是一种解决方案。
3. 改变一个哲学家拿筷子的顺序
我们还可以利用死锁避免策略那就是从逻辑上去避免死锁的发生比如改变其中一个哲学家拿筷子的顺序。我们可以让 4 个哲学家都先拿左边的筷子再拿右边的筷子但是有一名哲学家与他们相反他是先拿右边的再拿左边的这样一来就不会出现循环等待同一边筷子的情况也就不会发生死锁了。
死锁解决 我们把“改变一个哲学家拿筷子的顺序”这件事情用代码来写一下修改后的 main 方法如下 Philosopher[] philosophers new Philosopher[5];Object[] chopsticks new Object[philosophers.length];for (int i 0; i chopsticks.length; i) {chopsticks[i] new Object();}for (int i 0; i philosophers.length; i) {Object leftChopstick chopsticks[i];Object rightChopstick chopsticks[(i 1) % chopsticks.length];if (i philosophers.length - 1){philosophers[i] new Philosopher(rightChopstick, leftChopstick);}else{philosophers[i] new Philosopher(leftChopstick, rightChopstick);}
// philosophers[i] new Philosopher(rightChopstick, leftChopstick);new Thread(philosophers[i], 哲学家 (i 1) 号).start();}if (i philosophers.length - 1) 在这种情况下我们给它传入的筷子顺序恰好相反这样一来他拿筷子的顺序也就相反了他会先拿起右边的筷子再拿起左边的筷子。那么这个程序运行的结果是所有哲学家都可以正常地去进行思考和就餐了并且不会发生死锁。