一、synchronized死鎖
我們先來看以下代碼
示例
- A線程
package com.it.test.thread.consumer_product.dielock;
public class AThread extends Thread{
private Food food;
private Water water;
public AThread(Food food, Water water) {
this.food = food;
this.water = water;
}
@Override
public void run() {
synchronized (water){
System.out.println("AThread拿到水了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (food){
System.out.println("AThread拿到食物了");
}
}
}
}
- B線程
public class BThread extends Thread{
private Food food;
public Water water;
public BThread(Food food, Water water) {
this.food = food;
this.water = water;
}
@Override
public void run() {
synchronized (food){
System.out.println("BThread拿到食物了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (water){
System.out.println("BThread拿到水了");
}
}
}
}
- 測試
package com.it.test.thread.consumer_product.dielock;
public class DieLockTest {
public static void main(String[] args) {
Food food = new Food();
Water water = new Water();
AThread aThread = new AThread(food,water);
BThread bThread = new BThread(food,water);
aThread.start();
bThread.start();
}
}
AThread拿到水了
BThread拿到食物了
代碼的設計是A線程拿到水,接着去拿食物,然後執行完畢。B線程先拿食物,然後拿水,然後執行完畢。
但是由於A線程和B線程拿線程的資源不對,導致A線程拿到了水之後,一直在等食物,卻沒有釋放水。B線程拿到食物之後,一直在等待水,沒有釋放食物。導致兩個線程處於一直等待拿鎖的狀態。這樣就造成了死鎖
解決辦法
我們將A線程和B線程拿資源的順序修改一下,使得他們都是先拿水,在拿食物試試。
package com.it.test.thread.consumer_product.dielock;
public class AThread extends Thread{
private Food food;
private Water water;
public AThread(Food food, Water water) {
this.food = food;
this.water = water;
}
@Override
public void run() {
synchronized (water){
System.out.println("AThread拿到水了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (food){
System.out.println("AThread拿到食物了");
}
}
}
}
package com.it.test.thread.consumer_product.dielock;
public class BThread extends Thread{
private Food food;
public Water water;
public BThread(Food food, Water water) {
this.food = food;
this.water = water;
}
@Override
public void run() {
synchronized (water){
System.out.println("BThread拿到水了");
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (food){
System.out.println("BThread拿到食物了");
}
}
}
}
AThread拿到水了
AThread拿到食物了
BThread拿到水了
BThread拿到食物了
二、產生死鎖的條件
(1)多個線程搶佔多個資源
(2)搶佔資源的順序不對
(3)拿到資源不放手
三、使用ReentrantLock嘗試拿鎖的機制解決死鎖問題
- 創建兩個線程爭奪的資源實體,繼承ReentrantLock。
package com.it.test.thread.consumer_product.dielock;
import java.util.concurrent.locks.ReentrantLock;
public class Food extends ReentrantLock {
}
package com.it.test.thread.consumer_product.dielock;
import java.util.concurrent.locks.ReentrantLock;
public class Water extends ReentrantLock {
}
- 創建兩個線程競爭嘗試獲取資源
- AThread
package com.it.test.thread.consumer_product.dielock;
import java.util.Random;
public class AThread extends Thread{
private Food food;
private Water water;
public AThread(Food food, Water water) {
this.food = food;
this.water = water;
}
@Override
public void run() {
Random r = new Random();
while (true)
{
if(food.tryLock())
{
System.out.println("AThread拿到食物了");
if(water.tryLock()){
System.out.println("AThread拿到水了");
food.unlock();
water.unlock();
//跳出循環
break;
}
else{
System.out.println("AThread沒有拿到水,放棄了食物");
food.unlock();
}
}
try {
Thread.sleep(r.nextInt(3));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
- BThread
package com.it.test.thread.consumer_product.dielock;
public class BThread extends Thread{
private Food food;
public Water water;
public BThread(Food food, Water water) {
this.food = food;
this.water = water;
}
@Override
public void run() {
while (true){
if(water.tryLock())
{
System.out.println("BThread拿到水了");
if(food.tryLock())
{
System.out.println("BThread拿到食物了");
food.unlock();
water.unlock();
//跳出循環
break;
}
else{
System.out.println("BThread沒有拿到食物,放棄了水");
water.unlock();
}
}
}
}
}
- 測試
package com.it.test.thread.consumer_product.dielock;
public class DieLockTest {
public static void main(String[] args) {
Food food = new Food();
Water water = new Water();
AThread aThread = new AThread(food,water);
BThread bThread = new BThread(food,water);
aThread.start();
bThread.start();
}
}
BThread拿到水了
AThread拿到食物了
BThread沒有拿到食物,放棄了水
AThread沒有拿到水,放棄了食物
BThread拿到水了
BThread沒有拿到食物,放棄了水
AThread拿到食物了
AThread沒有拿到水,放棄了食物
BThread拿到水了
BThread拿到食物了
AThread拿到食物了
AThread拿到水了
在最後A線程和B線程都拿到了食物和水。
在嘗試拿鎖的機制中,A線程和B線程中都開啓了一個while循環,只有水和食物都拿到之後,才跳出循環。A線程嘗試先拿食物,拿到食物之後嘗試拿水,如果拿不到水,則將原來的食物也丟棄。B線程也一樣,嘗試先拿食物,拿到食物後再拿水,如果拿不到水,則將原來的食物也丟棄。在嘗試拿鎖的過程,我們給A線程做出一點點隨機的休眠時間,錯開。最後A和B線程都會拿到食物和水。
嘗試拿鎖的機制,就會產生一個問題就是:每次嘗試拿鎖的時候,A線程都是先拿到食物,B線程拿到水。他們在往下面再拿資源的時候,拿不到。然後就放棄原來的鎖,重新拿。因此在程序上我們就做了一個時間的錯開。這種一直嘗試拿鎖,又放棄鎖的現象我們稱爲活鎖。