Java 學習記錄105 — Deadlock

張小雄
7 min readJan 15, 2022

--

今天要學的是 Deadlock

Deadlocks.java

import java.util.Random;public class Deadlocks {
public static void main(String[] args) {
Message message = new Message();
(new Thread(new Writer(message))).start();
(new Thread(new Reader(message))).start();
}
}
class Message {
private String message;
private boolean empty = true;
public synchronized String read() {
while (empty) {
}
empty = true;
return message;
}
public synchronized void write(String message) {
while (!empty) {
}
empty = false;
this.message = message;
}
}class Writer implements Runnable {
private Message message;
public Writer(Message message) {
this.message = message;
}
public void run() {
String messages[] = {
"Humpty Dumpty sat on a wall",
"Humpty Dumpty had a great fall",
"All the king's horse and all the king's men",
"Couldn't put Humpty together again."
};
Random random = new Random(); for (int i = 0; i < messages.length; i++) {
message.write(messages[i]);
try {
Thread.sleep(random.nextInt(2000));
} catch (InterruptedException e) {
}
}
message.write("Finished");
}
}
class Reader implements Runnable {
private Message message;
public Reader(Message message) {
this.message = message;
}
public void run() {
Random ramdom = new Random();
for (String latestMessage = message.read(); !latestMessage.equals("Finished");
latestMessage = message.read()) {
System.out.println(latestMessage);
try {
Thread.sleep(random.nextInt(2000));
} catch (InterruptedException e) {
}
}
}
}

輸出結果:

Humpty Dumpty sat on a wall

這裡做了兩個 Thread,有 implements Runnable 的兩個就是,一個是讀取,一個是寫入

可以看到在 Message 裡面的兩個 Method 都有使用 synchronized

而輸出結果只顯示了一句,照理說要四句都顯示出來

這裡的條件是要等待 emptybool 改變才能跳出 while

但因為使用了 synchronized 造成第一個 Thread 進去後,其他 Thread 在外面等

但因為沒有達成退出條件,所以就一直卡在 while 裡面,程序就進入永遠的循環

這就是所謂的 Deadlock

資料補充:Deadlock

修改 Deadlocks.java

import java.util.Random;public class Deadlocks {
public static void main(String[] args) {
Message message = new Message();
(new Thread(new Writer(message))).start();
(new Thread(new Reader(message))).start();
}
}
class Message {
private String message;
private boolean empty = true;
public synchronized String read() {
while (empty) {
try {
wait();
} catch (InterruptedException e) {
}
}
empty = true;
notifyAll();
return message;
}
public synchronized void write(String message) {
while (!empty) {
try {
wait();
} catch (InterruptedException e) {
}
}
empty = false;
this.message = message;
notifyAll();
}
}class Writer implements Runnable {
private Message message;
public Writer(Message message) {
this.message = message;
}
public void run() {
String messages[] = {
"Humpty Dumpty sat on a wall",
"Humpty Dumpty had a great fall",
"All the king's horse and all the king's men",
"Couldn't put Humpty together again."
};
Random random = new Random(); for (int i = 0; i < messages.length; i++) {
message.write(messages[i]);
try {
Thread.sleep(random.nextInt(2000));
} catch (InterruptedException e) {
}
}
message.write("Finished");
}
}
class Reader implements Runnable {
private Message message;
public Reader(Message message) {
this.message = message;
}
public void run() {
Random random = new Random();
for (String latestMessage = message.read(); !latestMessage.equals("Finished");
latestMessage = message.read()) {
System.out.println(latestMessage);
try {
Thread.sleep(random.nextInt(2000));
} catch (InterruptedException e) {
}
}
}
}

輸出結果:

Humpty Dumpty sat on a wall

Humpty Dumpty had a great fall

All the king’s horse and all the king’s men

Couldn’t put Humpty together again.

因此這邊使用了 wait()notifyAll() 來解決此問題

前者可以當作是先在這邊暫停並等待喚醒而後者就是喚醒前者

一開始沒有使用這兩者的時候,程序卡在 read() 裡面

當第二次 for 的時候,因為第一次已經把 empty 改成 true

也沒有外力可以改變,所以就卡死在那邊

當這次使用了這兩者,就變成到 wait() 之時,先暫停執行並解鎖,好讓程序繼續執行

而此時 write() 那邊,因為可以繼續往下走就把 empty 改成 false

之後又走到 notifyAll()read() 那邊喚醒

此時一看 empty 已經改變就順利跳出 while 了,所以不會繼續卡死在那邊

上面代碼全都紀錄在我的 Github

--

--

張小雄
張小雄

Written by 張小雄

記錄成為軟體工程師的過程

No responses yet