3个方面回答 :
1.死锁是什么?
提供了几种理解的方式帮助大家更好的理解;如有疑问可提出;
死锁是一组相互竞争资源的线程因为他们之间得到互相等待导致“永久“阻塞的现象; (你等我 我等你 你不放我也不放 就导致“永久“阻塞的现象)
死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程
了解了说你是死锁后我们要知道为什么会发生死锁呢,请看下面;
2.发生死锁的原因?
分为下面4钟原因
互斥条件 |
共享资源 X y 只能被一个线程占有 |
占用且等待 |
线程T1占用的共享资源X 他在等待共享Y的时候帮不释放自己的X |
不可抢占 |
其他线程不能去抢占t1线程占有的资源 |
循环等待 |
线程t1 等t2 占有的资源,线程t2 等t1占有的资源 循环等等 |
3.如何避免死锁?
打破死锁的原因即可避免
互斥条件 |
无法破坏的因为锁本身就是通过互斥解决线程的安全的问题 |
占用且等待 |
一次性申请所有的资源就不会存在等待了 |
不可抢占 |
占用部分资源的线程,进一步申请其他的资源的时候如果申请不到可以主动释放他占有的资源,这样就破坏了不可抢占 |
循环等待 |
按照顺序申请资源进行预防,所谓按序申请是指资源是有线性顺序的,申请的时候可以先申请资源序号小的 在申请资源序号大的 ,这样线性化循环就不存在等待了 |
下面我们用代码理解下死锁:
package com.example; public class Deadlock { public static String str1 = "str1"; public static String str2 = "str2"; public static void main(String[] args){ Thread a = new Thread(() -> { try{ while(true){ synchronized(Deadlock.str1){ System.out.println(Thread.currentThread().getName()+"锁住 str1"); Thread.sleep(1000); synchronized(Deadlock.str2){ System.out.println(Thread.currentThread().getName()+"锁住 str2"); } } } }catch(Exception e){ e.printStackTrace(); } }); Thread b = new Thread(() -> { try{ while(true){ synchronized(Deadlock.str2){ System.out.println(Thread.currentThread().getName()+"锁住 str2"); Thread.sleep(1000); synchronized(Deadlock.str1){ System.out.println(Thread.currentThread().getName()+"锁住 str1"); } } } }catch(Exception e){ e.printStackTrace(); } }); a.start(); b.start(); } }
上面的代码就是一个完整的死锁程序,程序中有两个线程,线程1锁住了str1,获得锁之后休眠1秒钟,这个时候线程2锁住了str2,也进行休眠操作。
线程1休眠完了之后去锁str2,但是str2已经被线程2给锁住了,这边只能等待,同样的道理,线程2休眠完之后也要去锁str1,同样也会等待,这样死锁就产生了。
将下面这2行代码的值改成一样,就不会存在了
public static String str1 = "str1"; public static String str2 = "str1";
在声明一个对象作为锁的时候要注意字符串类型锁对象,因为字符串有一个常量池,如果不同的线程持有的锁是具有相同字符的字符串锁时,两个锁实际上同一个锁。