什么是死锁,简单来说就是两个或多个线程互相等待对方持有的锁,从而陷入无限等待的状态 。
死锁场景一:
一个线程对同一把锁连续加锁2次。
在上述代码中t1线程对同一个锁对象连续加锁了两次,当一个线程加上了锁,其他要加上这把锁的线程只能阻塞等待这个线程释放锁才能加上锁,所以上述代码中,t1线程先是用第一个synchronized(lock)加上了锁,后面第二个synchronized(lock)想继续加上锁只能阻塞等待第一个synchronized(lock)释放锁,可是第一个synchronized(lock)要想释放锁就要退出第一个synchronized(lock)修饰的代码块,但是想要执行完第一个synchronized(lock)修饰的代码块,就必须让第二个synchronized(lock)加上锁,这就导致了t1线程陷入无限等待的状态,从而导致了死锁。
上述代码结果:
上述代码运行的结果并未和我们上述的分析一样陷入无限等待的状态,而是执行完线程t1的run方法后结束线程,这是因为在Java中的synchronized关键字做了特殊的处理,synchronized是可重入锁,synchronized同步块对同⼀条线程来说是可重入的,不会出现自己把自己锁死的问题。简单来说就是synchronized在加锁的时候会判断当前加锁的线程和已经加上锁的线程是不是同一个线程如果是同一个线程不会真的加锁,而是会继续执行代码,所以才会出现上述结果。
死锁场景二:
两个线程,两把锁,线程t1先对锁1加锁,线程t2对锁2加锁,线程t1不释放锁1的情况下对锁2加锁,线程t2不释放锁2的情况下对锁1加锁。
运行结果:
从运行结果可以看出,上述代码一直在阻塞等待,造成了死锁 。上述代码首先创建线程t1与线程t2,线程t1对锁1加锁后sleep了0.5s,线程t2对锁2加锁后sleep0.5s,这时线程t1要继续对锁2加锁,可是锁2这时被线程t2加上锁,线程t1只能阻塞等待线程t2释放锁,但是,线程t2要释放锁就要对锁1加上锁,也要阻塞等待线程t1释放锁,这就导致了线程t1与线程t2在互相阻塞等待,导致了死锁。
死锁场景三:
又n个线程m把锁。下面用一个经典模型 '哲学家就餐问题' 来举例说明
根据上述情况分析,当这位5位哲学家同时拿起左手边的筷子,这时每位哲学家都拿到一把筷子,但是没有吃饱不会放下手中的筷子,这就导致这5位哲学家在互相等待对方的筷子但不会放下自己手中的筷子,这就陷入无限等待的状态,要是把哲学家联想成线程,筷子联想成锁,这种情况就导致了死锁。