多个线程各自占有一些共享资源,并且互相等待着其它线程占有资源使用释放锁,才能运行,而导致两个或者多个线程都在等待对方释放锁,最后会出现都停止执行的情形**(假死)。某一个同步块 同时拥有 “两个以上对象的锁” 时,就可能会发生 "死锁"问题。 (切记 这个的意思就是指 在一个同步锁 里面 又嵌套了一个 同步锁!)
举例:两个线程互相 想操作对方的资源,但是双方都不想给,就形成了死锁。(一个同步块 里面还有一个 同步块。)
原因:时间切换频率太快,可能两个线程 一下子直接就获取了双方 锁住的东西。然后造成了 双阻塞。这样就会造成 假死!JVM 会 自动终止程序! 这就是死锁现象
package www.muquanyu.syn; //死锁:多个线程互相持有着对方 需要的资源,但是都不愿意给。 //最后形成了僵持! public class DeadLock { public static void main(String[] args) { Makeup 白雪公主 = new Makeup(0, "白雪公主"); Makeup 皇后 = new Makeup(1, "皇后"); new Thread(白雪公主).start(); new Thread(皇后).start(); } } //口红 class Lipstick{ } //镜子 class Mirror{ } class Makeup extends Thread{ //两个资源 static Lipstick lipstick = new Lipstick(); static Mirror mirror = new Mirror(); int choice;//选择 String girlName;//使用化妆品的人 public Makeup(int choice,String girlName) { this.choice = choice; this.girlName = girlName; } @Override public void run() { super.run(); //化妆 try { makeup(); } catch (InterruptedException e) { e.printStackTrace(); } } //化妆:就是需要拿到对方的资源 private void makeup() throws InterruptedException { if(choice == 0) { synchronized(lipstick){//获得了口红的锁 System.out.println(this.girlName +"获得了口红的锁"); Thread.sleep(1); synchronized(mirror){//然后又想操作镜子资源 //获取到了 镜子的锁 System.out.println(this.girlName + "获得了镜子的锁"); } } } else{ synchronized(mirror){//获取到了 镜子的锁 System.out.println(this.girlName +"获得了镜子的锁"); Thread.sleep(2); synchronized(lipstick){//然后又想操作口红资源 //获得了口红的锁 System.out.println(this.girlName +"获得了口红的锁"); } } } } }
你会发现 它们 输出的信息 并不完全,这是因为 发生了 死锁现象,彼此只是获取到了自己本来就该有的资源!
解析:白雪公主想要 获取 镜子的锁时,发现 皇后没有 释放 镜子的锁。皇后想要获取 口红的时候,发现 白雪公主 没有释放 口红的锁。这就导致了,一瞬间 双方都 进入了 阻塞状态!
那么为什么 白雪公主 的口红锁 没有释放呢? 这是因为 口红锁的同步块里面 有个 镜子锁的同步块,而镜子锁无法获取!所以口红锁 的同步块 将不会执行完毕!所以 也就不会 释放口红锁。。。
解决死锁的方法,很简单。只需要 把同步块内部的那个 同步块 拿出来就行了。这样在 保证 释放 自己的锁的同时,也能够 获取到 其它的锁,而且数据都是安全的!不会出现紊乱!!!
package www.muquanyu.syn; //死锁:多个线程互相持有着对方 需要的资源,但是都不愿意给。 //最后形成了僵持! public class DeadLock { public static void main(String[] args) { Makeup 白雪公主 = new Makeup(0, "白雪公主"); Makeup 皇后 = new Makeup(1, "皇后"); new Thread(白雪公主).start(); new Thread(皇后).start(); } } //口红 class Lipstick{ } //镜子 class Mirror{ } class Makeup extends Thread{ //两个资源 static Lipstick lipstick = new Lipstick(); static Mirror mirror = new Mirror(); int choice;//选择 String girlName;//使用化妆品的人 public Makeup(int choice,String girlName) { this.choice = choice; this.girlName = girlName; } @Override public void run() { super.run(); //化妆 try { makeup(); } catch (InterruptedException e) { e.printStackTrace(); } } //化妆:就是需要拿到对方的资源 private void makeup() throws InterruptedException { if(choice == 0) { synchronized(lipstick){//获得了口红的锁 System.out.println(this.girlName +"获得了口红的锁"); Thread.sleep(1); } synchronized(mirror){//然后又想操作镜子资源 //获取到了 镜子的锁 System.out.println(this.girlName + "获得了镜子的锁"); } } else{ synchronized(mirror){//获取到了 镜子的锁 System.out.println(this.girlName +"获得了镜子的锁"); Thread.sleep(2); } synchronized(lipstick){//然后又想操作口红资源 //获得了口红的锁 System.out.println(this.girlName +"获得了口红的锁"); } } } }
为什么这样子就可以解决 ?
答:那是因为这样子写 我们的白雪公主 不需要获取 镜子锁 就已经 把 口红锁给释放了。皇后呢,发现 口红锁被释放掉了,那么它就捡起了口红锁。所以 白雪公主 也就 得到了 镜子,而 皇后 得到了 口红!
<说白了就是 在我们互相操作资源的时候,双方 不要抱着自己的 锁不放!!那么为什么会抱着自己的锁不放呢?不就是因为 同步锁里面又嵌套了一个同步锁吗。>
互斥条件:一个资源每次 只能被一个 进程/线程使用。(如果多个进程/线程 使用一个资源,那么可能会产出现死锁现象和并发问题。)
请求与保持条件:一个 进程/线程 因请求资源而阻塞时,对方获取的资源保持不放。(比如说我拿了镜子,还想去拿口红,但是我拿的时候,就不想把镜子给你。你也就拿不到镜子。那么双方就持于一种僵持的状态。)
不剥夺条件:进程/线程 已获得的资源,在未使用完之前,不能强行剥夺。(这是肯定的,人家没使用完呢,你不能使。)
循环等待条件:若干个 进程/线程 之间形成一种头尾相接的循环等待资源关系。(这是死锁现象最明显的展现!)
我们只要能够 打破 上述中的 任意一个 或 多个条件 就可以避免死锁现象!!