การหยุดชะงักเกิดขึ้นเมื่อเธรด (หรืออะไรก็ตามที่แพลตฟอร์มของคุณเรียกหน่วยประมวลผลของมัน) ได้รับทรัพยากรโดยที่แต่ละทรัพยากรสามารถถูกเก็บไว้ได้ทีละเธรดเท่านั้นและยึดทรัพยากรเหล่านั้นในลักษณะที่ไม่สามารถยกเลิกการระงับได้และ มีความสัมพันธ์ "แบบวงกลม" บางอย่างระหว่างเธรดซึ่งแต่ละเธรดในการหยุดชะงักกำลังรอรับทรัพยากรบางส่วนที่มีเธรดอื่นอยู่
ดังนั้นวิธีที่ง่ายที่จะหลีกเลี่ยงการหยุดชะงักคือการให้บางส่วนการสั่งซื้อทั้งหมดไปยังแหล่งข้อมูลและกำหนดกฎว่าทรัพยากรเป็นเพียงการที่เคยได้มาจากกระทู้ในการสั่งซื้อ ในทางกลับกันการหยุดชะงักสามารถสร้างขึ้นโดยเจตนาโดยการรันเธรดที่ได้รับทรัพยากร แต่ไม่ได้รับตามลำดับ ตัวอย่างเช่น:
สองเธรดสองล็อค เธรดแรกรันลูปที่พยายามรับล็อกตามลำดับที่กำหนดเธรดที่สองรันลูปที่พยายามรับล็อกในลำดับตรงข้าม แต่ละเธรดจะคลายล็อกทั้งสองหลังจากได้รับการล็อกสำเร็จแล้ว
public class HighlyLikelyDeadlock {
static class Locker implements Runnable {
private Object first, second;
Locker(Object first, Object second) {
this.first = first;
this.second = second;
}
@Override
public void run() {
while (true) {
synchronized (first) {
synchronized (second) {
System.out.println(Thread.currentThread().getName());
}
}
}
}
}
public static void main(final String... args) {
Object lock1 = new Object(), lock2 = new Object();
new Thread(new Locker(lock1, lock2), "Thread 1").start();
new Thread(new Locker(lock2, lock1), "Thread 2").start();
}
}
ตอนนี้มีความคิดเห็นบางส่วนในคำถามนี้ที่ชี้ให้เห็นความแตกต่างระหว่างความเป็นไปได้และความแน่นอนของการหยุดชะงัก ในบางแง่ความแตกต่างเป็นประเด็นทางวิชาการ จากมุมมองที่ใช้งานได้จริงฉันต้องการเห็นระบบการทำงานที่ไม่หยุดชะงักด้วยรหัสที่ฉันเขียนไว้ด้านบน :)
อย่างไรก็ตามคำถามในการสัมภาษณ์อาจเป็นคำถามเชิงวิชาการได้ในบางครั้งและคำถาม SO นี้จะมีคำว่า "แน่นอน" อยู่ในชื่อเรื่องดังนั้นสิ่งที่ตามมาคือโปรแกรมที่หยุดชะงักอย่างแน่นอน Locker
มีการสร้างอ็อบเจ็กต์สองอ็อบเจ็กต์โดยแต่ละอ็อบเจ็กต์จะได้รับสองล็อกและCountDownLatch
ใช้เพื่อซิงโครไนซ์ระหว่างเธรด แต่ละLocker
ล็อคล็อคแรกจากนั้นนับสลักหนึ่งครั้ง เมื่อเธรดทั้งสองได้รับการล็อกและนับถอยหลังสลักพวกเขาจะดำเนินการผ่านอุปสรรคของสลักและพยายามที่จะได้รับการล็อกครั้งที่สอง แต่ในแต่ละกรณีเธรดอีกอันหนึ่งมีการล็อกที่ต้องการอยู่แล้ว ส่งผลให้สถานการณ์นี้ในบางหยุดชะงัก
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class CertainDeadlock {
static class Locker implements Runnable {
private CountDownLatch latch;
private Lock first, second;
Locker(CountDownLatch latch, Lock first, Lock second) {
this.latch = latch;
this.first = first;
this.second = second;
}
@Override
public void run() {
String threadName = Thread.currentThread().getName();
try {
first.lock();
latch.countDown();
System.out.println(threadName + ": locked first lock");
latch.await();
System.out.println(threadName + ": attempting to lock second lock");
second.lock();
System.out.println(threadName + ": never reached");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
public static void main(final String... args) {
CountDownLatch latch = new CountDownLatch(2);
Lock lock1 = new ReentrantLock(), lock2 = new ReentrantLock();
new Thread(new Locker(latch, lock1, lock2), "Thread 1").start();
new Thread(new Locker(latch, lock2, lock1), "Thread 2").start();
}
}