目录
1.一个线程对应一把锁
一个线程对锁对象加锁后,忘记解锁,等自己再次需要对该锁对象加锁时(忘记是自己加锁的),发现锁已经被占用,于是就一直阻塞等待,但是自己加的锁必须由自己释放,这就会出现死锁的状态.,就是没有人来解锁,线程一直阻塞等待.
举个例子:
我去商场买衣服,在试衣间换衣服的时候把门锁了,换完不知道怎么的我就从试衣间出来了,而且我忘记了是自己上的锁,等我挑好衣服再次进试衣间发现进不去了,以为有人就一直等待,但是这个时候试衣间里没有人,我在门外也解不了锁,这时候就是死锁的状态,没有人来解锁.我就一直阻塞等待.
注:synchronized锁是可重入锁(连续对同一锁对象加锁两次不会出现死锁状态),不会出现这种状态.
2.两个线程对应两把锁
线程A对锁B进行加锁,线程B对锁A进行加锁.当线程A尝试对锁A进行加锁的时候,发现锁A被占用,所以阻塞等待(此时锁B还未释放),而线程B尝试对锁B进行加锁发现锁B被占用,又阻塞等待(此时锁A还未释放),这样线程A就会等线程B把锁A释放,线程B就会等线程A把锁B释放,两个一起阻塞等待,这时候也没有人去解锁,也产生了死锁的状态.
举个例子:
很经典一个例子就是你的车钥匙在家里,大门钥匙在车里,要想进家里就要打开车门,但是打开车门又要打开你家大门.这样门和车门都打不开,这就出现死锁状态.
3.多个线程对应多把锁
多个线程中的每个线程都在阻塞等待对应已经被占用的锁释放,而每个线程自己占用的锁都还未释放(别的线程也许在阻塞等待它们把自己占用的锁释放).线程A占用A锁,线程B占用B锁,线程C占用C锁. 线程C要对锁A加锁解锁后才能释放锁C,但是锁A被占用,它就一直阻塞等待锁A;而线程A要对锁B加锁解锁后才能释放锁A,但是锁B也被占用,它就一直阻塞等待锁B;线程B要对锁C加锁解锁后才能释放锁B,但是锁C也被占用,它就一直阻塞等待锁C.这样线程A等待线程B将锁B释放,线程B等待线程C将锁C释放,线程C等待线程A将锁A释放.它们一直阻塞等待,没人解锁,出现死锁状态.
举个例子
三个人(甲,乙,丙)同吃一碗面,桌上有三根筷子(编号分别为1,2,3),甲拿到1号筷子,还不能吃到面,因为它只拿了一根筷子,这时候乙拿到了2号筷子,也不能吃到面,突然丙抢到了3号筷子,那你觉得它们还能吃到面吗?每个人都只拿到了一根筷子,且它们都在等旁边的放下筷子好去抢离自己最近的筷子.有了一双筷子它们自己吃了面才能将一双筷子都放回桌子上.这样死等谁都吃不到面,所以就出现死锁的状态.
4.产生死锁的四个必要条件
① 不可抢占: 哪个线程加的锁只能由它自己进行释放,其它线程不能抢占.
② 互斥使用: 一个线程先抢到锁之后,其它线程只能阻塞等待.
③ 请求和保持: 一个线程占用一个锁的情况下,还要抢另一个锁.
④ 循环等待: 线程间的解锁都互相依赖,就像上述中甲乙丙三个人要吃到面放下筷子,三个人循环依赖的关系.
注:四个条件只有循环等待最好破解.
5.如何破解死锁?
很简单,直接规定加锁的顺序,线程只能先加序号小的锁,再加序号大的锁,在甲乙丙的例子中,也就是甲先对1号筷子进行加锁,乙对2号筷子加锁,丙的两边1号筷子序号最小,它尝试对1号筷子加锁,但是1号筷子已经被占用,而乙抢了2号筷子之后,就能直接抢到3号筷子,吃完面放下筷子,然后甲就能对2号筷子进行加锁,也吃完面放下筷子,最后丙就可以先对1号加锁,再对3号筷子进行加锁,吃完面也放下筷子.它们的位置如图:
分享完毕~