专栏首页渔夫Java并发-死锁

Java并发-死锁

一、死锁的简单概念

 所谓死锁是指两个或两个以上的线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无其余方法作用,它们都将无法推进下去。  网友们有一个生动形象的比方:两个人面对面过独木桥,甲和乙都已经在桥上走了一段距离,即占用了桥的资源,甲如果想通过独木桥的话,乙必须退出桥面让出桥的资源,让甲通过,但是乙不服,为什么让我先退出去,我还想先过去呢,于是就僵持不下,导致谁也过不了桥,这就是死锁。其实死锁形成的关键就是:谁也不让谁,谁都不会主动地让步。


二、死锁的简单例子

例子1:

//类1,d1方法为需要获得类1以及类2对象的方法
class DeadLock {
	private OtherService otherService;

	private static final Object LOCK = new Object();

	public DeadLock(OtherService otherService){
		this.otherService = otherService;
	}

	public void d1(){
		synchronized (LOCK){
			System.out.println("d1===========");
			otherService.o2();
		}
	}

	public void d2(){
		synchronized (LOCK){
			System.out.println("d2===========");
		}
	}

}

//类1,o1方法为需要获得类1以及类2对象的方法
class OtherService {
	private DeadLock deadLock;

	private static final Object LOCK = new Object();

	public void o1() {
		synchronized (LOCK){
			System.out.println("o1===========");
			deadLock.d2();
		}
	}

	public void o2() {
		synchronized (LOCK){
			System.out.println("o2===========");
		}
	}

	public void setDeadLock(DeadLock deadLock) {
		this.deadLock = deadLock;
	}
}


public class DeadLockTest {
	public static void main(String[] args) {
		OtherService otherService = new OtherService();
		DeadLock deadLock = new DeadLock(otherService);
		otherService.setDeadLock(deadLock);

		/**
		 * cmd通过jps命令查看该main线程的端口号
		 * 再通过jstack + 上面查到的端口号的命令查看栈情况
		 * 可以分析出来死锁情况:是否存在死锁
		 */
		new Thread(()->{
			while (true){
				deadLock.d1();
			}
		}).start();
		new Thread(()->{
			while (true){
				otherService.o1();
			}
		}).start();

	}

}

 上述代码描述了Java中死锁最简单的情况,一个线程Thread-0持有锁L0并且申请获得锁L1,而另一个线程Thread-1持有锁L1并且申请获得锁L0,因为默认的锁申请操作都是阻塞的,所以线程Thrad-0和Thread-1永远被阻塞了。导致了死锁。  虽然这种僵持情况由于线程程序运行时间可能错开,而不在程序运行的开始马上发生,但是这种结构的程序,若无其他代码进行死锁去除保障,那么死锁现象一定会发生。

例子2:

public class DeadLock_join {
    public static void main(String[] args) {
        System.out.println("main线程开始运行");

        try {
            Thread.currentThread().join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("如果打印出这段话则证明没有死锁");
    }
}

我们可以查看控制台观察到语句:如果打印出这段话则证明没有死锁永远得不到执行。这也是死锁的一个典型例子: 使用自己的线程对象作为同步锁,调用join方法,那么会造成死锁。


三、死锁的CMD查阅操作演示:

  1. cmd通过jps命令查看该main线程的端口号 Win+R,输入cmd,即可保证打开CMD端口。输入jps,根据main方法所在类的名字,找到线程的端口号。
  1. 再通过jstack + 上面查到的端口号的命令查看栈情况

1)可以看到线程Thread-0以及Thread-1线程发生了阻塞情况,即图中最后一行的Found 1 deadlock提示所示。 2) -locked则表示线程目前锁占据的锁ID,而waiting to lock后加ID代表被当前线程锁等待的,其他线程占据的未释的锁。

 我们发现线程Thread-0锁占据的锁,正是Thread-1锁等待的,而Thread-0等待的锁恰好是Thread-1锁占据的锁,除非线程等待的锁被其他线程释放了,那么当前线程才会释放自己的锁,那么其他线程才有机会获得当前线程的锁。恰恰因为线程的不主动让步,形成了死锁,2个线程”卡住了“。

注意事项: 很多人可能在CMD上输入jps/jstack等命令却不被系统识别,一般都是JDK环境在Win系统安装不正确导致,建议重新j检查环境变量是否安装正确。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • ThreadLocal 类精讲

    Context,上下文,其通常指能够提供环境、临时存储数据的实例。ThreadLocal 是一种上下文实例,Thread 可以通过调用其 set() 以及 ge...

    Fisherman渔夫
  • Java并发-守护线程-Daemon

     我们如上在main线程中定义了一个子线程t,将子线程的run方法写为sleep调用比main方法的sleep调用更久时间,以验证父类线程main的执行完毕并释...

    Fisherman渔夫
  • Java并发-被动使线程wait/notify

     此任务的目标是安全、高效地使一个运行着的线程开始进入wait状态以及从wait状态中唤醒。而此操作都是通过其他线程被动地使当前线程将运行状态的强制转换,具体实...

    Fisherman渔夫
  • 43道多线程面试题,附带答案(三)

    Volatile变量可以确保先行关系,即写操作会发生在后续的读操作之前, 但它并不能保证原子性。例如用volatile修饰count变量那么 count++ 操...

    李红
  • 43道多线程面试题,附带答案(三)

    Volatile变量可以确保先行关系,即写操作会发生在后续的读操作之前, 但它并不能保证原子性。例如用volatile修饰count变量那么 count++ 操...

    李红
  • ThreadLocal 类精讲

    Context,上下文,其通常指能够提供环境、临时存储数据的实例。ThreadLocal 是一种上下文实例,Thread 可以通过调用其 set() 以及 ge...

    Fisherman渔夫
  • 16.Java线程案例

    Java 实例 - 查看线程是否存活 以下实例演示了如何通过继承 Thread 类并使用 isAlive() 方法来检测一个线程是否存活: TwoThreadA...

    奋斗蒙
  • 多线程之死锁就是这么简单

    Java3y
  • 什么是线程死锁?形成条件是什么?如何避免?

    死锁是指两个或两个以上的进程(线程)在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态...

    程序员追风
  • 16.Java线程案例

    16.Java线程案例 Java 实例 - 查看线程是否存活 以下实例演示了如何通过继承 Thread 类并使用 isAlive() 方法来检测一个线程是否存活...

    奋斗蒙

扫码关注云+社区

领取腾讯云代金券