เขียนโปรแกรมที่จะเข้าสู่ภาวะชะงักงัน [ปิด] อย่างแน่นอน


86

ฉันเพิ่งได้รับคำถามนี้ในการสัมภาษณ์

ฉันตอบว่าการหยุดชะงักเกิดขึ้นหากการสอดประสานผิดพลาด แต่ผู้สัมภาษณ์ยืนยันว่าโปรแกรมที่จะเข้าสู่ภาวะชะงักงันโดยไม่คำนึงถึงการแทรกสอดสามารถเขียนได้

เราเขียนโปรแกรมดังกล่าวได้หรือไม่? ช่วยชี้ตัวอย่างโปรแกรมแบบนั้นให้หน่อยได้ไหม


3
ผู้สัมภาษณ์เป็นเพื่อนที่โง่เขลาแน่นอน
สิงโต

23
ผู้สัมภาษณ์ไม่ใช่เพื่อนที่โง่เขลาอย่างแน่นอน ความเข้าใจอย่างถ่องแท้เกี่ยวกับหัวเรื่องหมายความว่าคุณควรจะสามารถอธิบายกรณีขอบขั้วได้: การตั้งโปรแกรมเพื่อไม่ให้ล็อคและต้องล็อกเสมอ
Yuriy Zubarev

คำตอบ:


100

UPDATE: คำถามนี้เป็นเรื่องของบล็อกของฉันในเดือนมกราคม 2013 ขอบคุณสำหรับคำถามดีๆ!


เราจะเขียนโปรแกรมที่จะเข้าสู่ภาวะชะงักงันได้อย่างไรไม่ว่าเธรดจะถูกกำหนดเวลาไว้อย่างไร?

นี่คือตัวอย่างใน C # โปรดทราบว่าโปรแกรมดูเหมือนจะไม่มีการล็อกและไม่มีข้อมูลที่แชร์มีเพียงตัวแปรท้องถิ่นตัวเดียวและสามคำสั่งและยังคงมีความแน่นอน 100% หนึ่งจะยากที่จะสร้างโปรแกรมที่ง่ายกว่าที่หยุดชะงักด้วยความแน่นอน

ออกกำลังกายกับผู้อ่าน # 1: อธิบายว่าการหยุดชะงักนี้เป็นอย่างไร (คำตอบอยู่ในความคิดเห็น)

ออกกำลังกายกับผู้อ่าน # 2: แสดงให้เห็นถึงการหยุดชะงักเดียวกันใน Java (คำตอบอยู่ที่นี่: https://stackoverflow.com/a/9286697/88656 )

class MyClass
{
  static MyClass() 
  {
    // Let's run the initialization on another thread!
    var thread = new System.Threading.Thread(Initialize);
    thread.Start();
    thread.Join();
  }

  static void Initialize() 
  { /* TODO: Add initialization code */ }

  static void Main() 
  { }
}

4
ความรู้ของฉันเกี่ยวกับทฤษฎี C # มี จำกัด แต่ฉันถือว่า classloader รับประกันว่าโค้ดนั้นรันเธรดเดี่ยวเหมือนใน Java ฉันค่อนข้างแน่ใจว่ามีตัวอย่างที่คล้ายกันใน Java Puzzlers
Voo

11
@ วู: คุณมีความจำที่ดี Neal Gafter - ผู้เขียนร่วมของ "Java Puzzlers" - และฉันได้นำเสนอโค้ดรุ่นนี้ที่ค่อนข้างคลุมเครือในการพูดคุย "C # Puzzlers" ของเราในงาน Oslo Developer Conference เมื่อสองสามปีก่อน
Eric Lippert

41
@Lieven: ตัวสร้างแบบคงที่ต้องรันไม่เกินหนึ่งครั้งและต้องรันก่อนการเรียกครั้งแรกไปยังเมธอดแบบคงที่ใด ๆ ในคลาส Main เป็นวิธีการคงที่ดังนั้นเธรดหลักจึงเรียกสแตติก ctor เพื่อให้แน่ใจว่ามันทำงานเพียงครั้งเดียว CLR จะนำตัวล็อกที่ไม่ได้คลายออกจนกว่าสแตติก ctor จะเสร็จสิ้น เมื่อ ctor เริ่มเธรดใหม่เธรดนั้นจะเรียกเมธอดแบบสแตติกด้วยดังนั้น CLR จึงพยายามล็อคเพื่อดูว่าจำเป็นต้องรัน ctor หรือไม่ เธรดหลักในขณะที่ "เข้าร่วม" เธรดที่ถูกบล็อกและตอนนี้เรามีการหยุดชะงักของเรา
Eric Lippert

33
@artbristol: ฉันไม่เคยเขียนโค้ด Java มากเท่าบรรทัดนี้เลย ฉันไม่เห็นเหตุผลที่จะเริ่มในตอนนี้
Eric Lippert

4
โอ้ฉันคิดว่าคุณมีคำตอบสำหรับการออกกำลังกาย # 2 ของคุณ ขอแสดงความยินดีที่ได้รับคะแนนโหวตจำนวนมากสำหรับการตอบคำถาม Java
artbristol

27

สลักที่นี่ช่วยให้แน่ใจว่าล็อคทั้งสองถูกยึดไว้เมื่อแต่ละเธรดพยายามล็อคอีกอัน:

import java.util.concurrent.CountDownLatch;

public class Locker extends Thread {

   private final CountDownLatch latch;
   private final Object         obj1;
   private final Object         obj2;

   Locker(Object obj1, Object obj2, CountDownLatch latch) {
      this.obj1 = obj1;
      this.obj2 = obj2;
      this.latch = latch;
   }

   @Override
   public void run() {
      synchronized (obj1) {

         latch.countDown();
         try {
            latch.await();
         } catch (InterruptedException e) {
            throw new RuntimeException();
         }
         synchronized (obj2) {
            System.out.println("Thread finished");
         }
      }

   }

   public static void main(String[] args) {
      final Object obj1 = new Object();
      final Object obj2 = new Object();
      final CountDownLatch latch = new CountDownLatch(2);

      new Locker(obj1, obj2, latch).start();
      new Locker(obj2, obj1, latch).start();

   }

}

น่าสนใจในการรัน jconsole ซึ่งจะแสดงให้คุณเห็นการหยุดชะงักในแท็บเธรดอย่างถูกต้อง


3
นั่นเป็นวิธีที่ดีที่สุด แต่ฉันจะแทนที่sleepด้วยสลักที่เหมาะสม: ในทางทฤษฎีเรามีสภาพการแข่งขันที่นี่ แม้ว่าเราจะแน่ใจได้ว่า 0.5 วินาทีก็เพียงพอแล้ว แต่ก็ไม่ดีเกินไปสำหรับงานสัมภาษณ์
alf

25

การหยุดชะงักเกิดขึ้นเมื่อเธรด (หรืออะไรก็ตามที่แพลตฟอร์มของคุณเรียกหน่วยประมวลผลของมัน) ได้รับทรัพยากรโดยที่แต่ละทรัพยากรสามารถถูกเก็บไว้ได้ทีละเธรดเท่านั้นและยึดทรัพยากรเหล่านั้นในลักษณะที่ไม่สามารถยกเลิกการระงับได้และ มีความสัมพันธ์ "แบบวงกลม" บางอย่างระหว่างเธรดซึ่งแต่ละเธรดในการหยุดชะงักกำลังรอรับทรัพยากรบางส่วนที่มีเธรดอื่นอยู่

ดังนั้นวิธีที่ง่ายที่จะหลีกเลี่ยงการหยุดชะงักคือการให้บางส่วนการสั่งซื้อทั้งหมดไปยังแหล่งข้อมูลและกำหนดกฎว่าทรัพยากรเป็นเพียงการที่เคยได้มาจากกระทู้ในการสั่งซื้อ ในทางกลับกันการหยุดชะงักสามารถสร้างขึ้นโดยเจตนาโดยการรันเธรดที่ได้รับทรัพยากร แต่ไม่ได้รับตามลำดับ ตัวอย่างเช่น:

สองเธรดสองล็อค เธรดแรกรันลูปที่พยายามรับล็อกตามลำดับที่กำหนดเธรดที่สองรันลูปที่พยายามรับล็อกในลำดับตรงข้าม แต่ละเธรดจะคลายล็อกทั้งสองหลังจากได้รับการล็อกสำเร็จแล้ว

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();
    }
}

3
ขออภัยที่อ้างถึง Linus ว่า "Talk ถูกโปรดแสดงรหัสให้ฉันดู" - เป็นงานที่ดีและยากกว่าที่คิดอย่างน่าประหลาดใจ
alf

2
เป็นไปได้ที่จะเรียกใช้โค้ดนี้โดยไม่มีการหยุดชะงัก
Vladimir Zhilyaev

1
ตกลงพวกคุณโหด แต่ฉันคิดว่าตอนนี้เป็นคำตอบที่สมบูรณ์
Greg Mattes

@GregMattes ขอบคุณ :) ไม่สามารถเพิ่มอะไร แต่ +1 และหวังว่าคุณมีความสนุกสนาน :)
alf

15

นี่คือตัวอย่าง Java โดยทำตามของ Eric Lippert:

public class Lock implements Runnable {

    static {
        System.out.println("Getting ready to greet the world");
        try {
            Thread t = new Thread(new Lock());
            t.start();
            t.join();
        } catch (InterruptedException ex) {
            System.out.println("won't see me");
        }
    }

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }

    public void run() {           
        Lock lock = new Lock();      
    }

}

4
ฉันคิดว่าการใช้วิธี join in run นั้นทำให้เข้าใจผิดเล็กน้อย แสดงให้เห็นว่าการเข้าร่วมอื่นนอกเหนือจากบล็อกแบบคงที่เป็นสิ่งจำเป็นเพื่อให้ได้การหยุดชะงักในขณะที่การหยุดชะงักเกิดจากคำสั่ง "new Lock ()" เขียน
ซ้ำ

คุณช่วยอธิบายตัวอย่างของคุณได้ไหม
gstackoverflow

ตามการทดลองของฉันt.join (); inside run () method ซ้ำซ้อน
gstackoverflow

ฉันลบรหัสซ้ำซ้อนซึ่งทำให้ไม่เข้าใจ
gstackoverflow

11

นี่คือตัวอย่างจากเอกสาร:

public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s"
                + "  has bowed to me!%n", 
                this.name, bower.getName());
            bower.bowBack(this);
        }
        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s"
                + " has bowed back to me!%n",
                this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse =
            new Friend("Alphonse");
        final Friend gaston =
            new Friend("Gaston");
        new Thread(new Runnable() {
            public void run() { alphonse.bow(gaston); }
        }).start();
        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
}

2
+1 สำหรับการเชื่อมโยงการสอน java
MRE

4
"มันมากมีแนวโน้มที่" ไม่ดีพอสำหรับ "ก็จะไปสู่การหยุดชะงัก"
alf

1
@ โอ้ แต่ปัญหาพื้นฐานแสดงให้เห็นได้ค่อนข้างดีที่นี่ เราสามารถเขียน Round robin Scheduler ซึ่งแสดงObject invokeAndWait(Callable task)วิธีการ แล้วสิ่งที่Callable t1ได้ทำคือinvokeAndWait()สำหรับCallable t2ในช่วงเวลาชีวิตของมันก่อนจะกลับมาและในทางกลับกัน
user268396

2
@ user268396 ปัญหาพื้นฐานเป็นเรื่องเล็กน้อยและน่าเบื่อ :) ประเด็นทั้งหมดของงานคือการค้นหาหรือพิสูจน์ว่าคุณเข้าใจว่าเป็นเรื่องยากที่จะได้รับการรับประกันการหยุดชะงัก (เช่นเดียวกับการรับประกันทุกสิ่งในโลกอะซิงโครนัส ).
alf

4
@bezz sleepน่าเบื่อ. แม้ว่าฉันเชื่อว่าจะไม่มีเธรดเริ่มต้นเป็นเวลา 5 วินาที แต่ก็เป็นเงื่อนไขการแข่งขันอยู่ดี คุณไม่ต้องการจ้างโปรแกรมเมอร์ที่ต้องพึ่งพาsleep()ในการแก้ไขเงื่อนไขการแข่งขัน :)
alf

9

ฉันได้เขียนตัวอย่างการหยุดชะงักเวอร์ชัน Java ของ Yuriy Zubarev ใหม่ที่โพสต์โดย Eric Lippert: https://stackoverflow.com/a/9286697/2098232ให้ใกล้เคียงกับเวอร์ชัน C # มากขึ้น หากบล็อกการเริ่มต้นของ Java ทำงานคล้ายกับตัวสร้างแบบคงที่ C # และได้รับการล็อคครั้งแรกเราไม่จำเป็นต้องมีเธรดอื่นเพื่อเรียกใช้วิธีการเข้าร่วมเพื่อรับการหยุดชะงักเพียงแค่ต้องเรียกใช้วิธีการแบบคงที่จากคลาสล็อคเช่น C # ดั้งเดิม ตัวอย่าง. การหยุดชะงักที่เกิดขึ้นดูเหมือนจะยืนยันสิ่งนี้

public class Lock {

    static {
        System.out.println("Getting ready to greet the world");
        try {
            Thread t = new Thread(new Runnable(){

                @Override
                public void run() {
                    Lock.initialize();
                }

            });
            t.start();
            t.join();
        } catch (InterruptedException ex) {
            System.out.println("won't see me");
        }
    }

    public static void main(String[] args) {
        System.out.println("Hello World!");
    }

    public static void initialize(){
        System.out.println("Initializing");
    }

}

ทำไมเมื่อฉันแสดงความคิดเห็น Lock.initialize () ในวิธีการเรียกใช้มันไม่ได้หยุดชะงัก? วิธีการเริ่มต้นไม่ได้ทำอะไรเลย ??
Aequitas

@Aequitas เป็นเพียงการคาดเดา แต่วิธีการนี้สามารถปรับให้เหมาะสมได้ ไม่แน่ใจว่าจะทำงานกับเธรดอย่างไร
Dave Cousineau

5

ไม่ใช่งานสัมภาษณ์ที่ง่ายที่สุดที่คุณจะได้รับ: ในโครงการของฉันมันทำให้งานของทีมเป็นอัมพาตไปทั้งวัน มันง่ายมากที่จะทำให้โปรแกรมของคุณหยุดทำงาน แต่มันยากมากที่จะทำให้มันอยู่ในสถานะที่เธรดดัมพ์เขียนอะไรเช่น

Found one Java-level deadlock:
=============================
"Thread-2":
  waiting to lock monitor 7f91c5802b58 (object 7fb291380, a java.lang.String),
  which is held by "Thread-1"
"Thread-1":
  waiting to lock monitor 7f91c6075308 (object 7fb2914a0, a java.lang.String),
  which is held by "Thread-2"

Java stack information for the threads listed above:
===================================================
"Thread-2":
    at uk.ac.ebi.Deadlock.run(Deadlock.java:54)
    - waiting to lock <7fb291380> (a java.lang.String)
    - locked <7fb2914a0> (a java.lang.String)
    - locked <7f32a0760> (a uk.ac.ebi.Deadlock)
    at java.lang.Thread.run(Thread.java:680)
"Thread-1":
    at uk.ac.ebi.Deadlock.run(Deadlock.java:54)
    - waiting to lock <7fb2914a0> (a java.lang.String)
    - locked <7fb291380> (a java.lang.String)
    - locked <7f32a0580> (a uk.ac.ebi.Deadlock)
    at java.lang.Thread.run(Thread.java:680)

ดังนั้นเป้าหมายคือการหยุดชะงักซึ่ง JVM จะพิจารณาการหยุดชะงัก เห็นได้ชัดว่าไม่มีทางออกเช่น

synchronized (this) {
    wait();
}

จะทำงานในแง่นั้นแม้ว่าพวกเขาจะหยุดตลอดไป การอาศัยสภาพการแข่งขันไม่ใช่ความคิดที่ดีเช่นกันเนื่องจากในระหว่างการสัมภาษณ์คุณมักต้องการแสดงบางสิ่งที่ใช้ได้ผลไม่ใช่สิ่งที่ควรได้ผลเกือบตลอดเวลา

ตอนนี้sleep()วิธีแก้ปัญหาก็โอเคในแง่หนึ่งมันยากที่จะจินตนาการถึงสถานการณ์ที่มันไม่ได้ผล แต่ก็ไม่ยุติธรรม (เราอยู่ในกีฬาที่ยุติธรรมไม่ใช่เหรอ?) การแก้ปัญหาโดย @artbristol (ของฉันเหมือนกันเพียงแค่วัตถุที่แตกต่างกันเป็นจอภาพ) นั้นดี แต่ก็ยาวและใช้ไพรเมอร์การทำงานพร้อมกันใหม่เพื่อให้เธรดอยู่ในสถานะที่ถูกต้องซึ่งไม่สนุกเท่าไหร่:

public class Deadlock implements Runnable {
    private final Object a;
    private final Object b;
    private final static CountDownLatch latch = new CountDownLatch(2);

    public Deadlock(Object a, Object b) {
        this.a = a;
        this.b = b;
    }

    public synchronized static void main(String[] args) throws InterruptedException {
        new Thread(new Deadlock("a", "b")).start();
        new Thread(new Deadlock("b", "a")).start();
    }

    @Override
    public void run() {
        synchronized (a) {
            latch.countDown();
            try {
                latch.await();
            } catch (InterruptedException ignored) {
            }
            synchronized (b) {
            }
        }
    }
}

ฉันจำได้ว่า synchronized - โซลูชันเดียวเหมาะกับรหัส 11..13 บรรทัด (ไม่รวมความคิดเห็นและการนำเข้า) แต่ยังไม่ได้นึกถึงเคล็ดลับจริง จะอัปเดตถ้าฉันทำ

อัปเดต: นี่คือวิธีแก้ปัญหาที่น่าเกลียดในsynchronized:

public class Deadlock implements Runnable {
    public synchronized static void main(String[] args) throws InterruptedException {
        synchronized ("a") {
            new Thread(new Deadlock()).start();
            "a".wait();
        }
        synchronized ("") {
        }
    }

    @Override
    public void run() {
        synchronized ("") {
            synchronized ("a") {
                "a".notifyAll();
            }
            synchronized (Deadlock.class) {
            }
        }
    }
}

โปรดทราบว่าเราแทนที่สลักด้วยการตรวจสอบวัตถุ (โดยใช้"a"เป็นวัตถุ)


ฉันคิดว่ามันเป็นงานสัมภาษณ์ที่ยุติธรรม ขอให้คุณเข้าใจการหยุดชะงักและการล็อกใน Java อย่างแท้จริง ฉันไม่คิดว่าความคิดทั่วไปก็ยากเช่นกัน (ตรวจสอบให้แน่ใจว่าเธรดทั้งสองสามารถดำเนินการต่อได้หลังจากที่ทั้งคู่ล็อกทรัพยากรแรกแล้ว) คุณควรจำ CountdownLatch - แต่ในฐานะผู้สัมภาษณ์ฉันจะช่วยผู้ให้สัมภาษณ์ในส่วนนั้น ถ้าเขาสามารถอธิบายสิ่งที่เขาต้องการได้ (ไม่ใช่คลาสที่ devs ส่วนใหญ่ต้องการและคุณไม่สามารถ google ให้สัมภาษณ์ได้) ฉันชอบที่จะได้รับคำถามที่น่าสนใจสำหรับการสัมภาษณ์!
Voo

@ วูในตอนที่เราเล่นมันไม่มีสลักใน JDK ดังนั้นมันจึงทำด้วยมือทั้งหมด และความแตกต่างระหว่างLOCKEDและwaiting to lockละเอียดอ่อนไม่ใช่สิ่งที่คุณอ่านระหว่างมื้อเช้า แต่คุณอาจจะพูดถูก ให้ฉันเรียบเรียงใหม่
alf

4

C # เวอร์ชั่นนี้ผมว่า java น่าจะคล้าย ๆ กัน

static void Main(string[] args)
{
    var mainThread = Thread.CurrentThread;
    mainThread.Join();

    Console.WriteLine("Press Any key");
    Console.ReadKey();
}

2
สิ่งที่ดี! โปรแกรม C # ที่สั้นที่สุดอย่างแท้จริงในการสร้างการหยุดชะงักหากคุณลบconsoleคำสั่ง คุณสามารถเขียนMainฟังก์ชันทั้งหมดเป็นThread.CurrentThread.Join();ไฟล์.
RBT

3
import java.util.concurrent.CountDownLatch;

public class SO8880286 {
    public static class BadRunnable implements Runnable {
        private CountDownLatch latch;

        public BadRunnable(CountDownLatch latch) {
            this.latch = latch;
        }

        public void run() {
            System.out.println("Thread " + Thread.currentThread().getId() + " starting");
            synchronized (BadRunnable.class) {
                System.out.println("Thread " + Thread.currentThread().getId() + " acquired the monitor on BadRunnable.class");
                latch.countDown();
                while (true) {
                    try {
                        latch.await();
                    } catch (InterruptedException ex) {
                        continue;
                    }
                    break;
                }
            }
            System.out.println("Thread " + Thread.currentThread().getId() + " released the monitor on BadRunnable.class");
            System.out.println("Thread " + Thread.currentThread().getId() + " ending");
        }
    }

    public static void main(String[] args) {
        Thread[] threads = new Thread[2];
        CountDownLatch latch = new CountDownLatch(threads.length);
        for (int i = 0; i < threads.length; ++i) {
            threads[i] = new Thread(new BadRunnable(latch));
            threads[i].start();
        }
    }
}

โปรแกรมจะหยุดชะงักเสมอเนื่องจากแต่ละเธรดกำลังรอที่สิ่งกีดขวางสำหรับเธรดอื่น ๆ แต่เพื่อที่จะรอสิ่งกีดขวางเธรดจะต้องเปิดจอภาพBadRunnable.classไว้


3
} catch (InterruptedException ex) { continue; }... สวยงาม
artbristol

2

มีตัวอย่างใน Java ที่นี่

http://baddotrobot.com/blog/2009/12/24/deadlock/

ที่ผู้ลักพาตัวเข้าสู่ภาวะชะงักงันเมื่อเขาปฏิเสธที่จะยอมแพ้เหยื่อจนกว่าเขาจะได้รับเงินสด แต่ผู้เจรจาปฏิเสธที่จะให้เงินสดจนกว่าเขาจะได้เหยื่อ


การนำไปใช้นั้นไม่เกี่ยวข้องกับที่ระบุ รหัสบางชิ้นดูเหมือนจะหายไป อย่างไรก็ตามความคิดทั่วไปที่คุณแสดงออกนั้นถูกต้องเท่าที่เกี่ยวข้องกับการแย่งชิงทรัพยากรที่นำไปสู่การหยุดชะงัก
Master Chief

ตัวอย่างนี้เป็นการเรียนการสอนดังนั้นฉันจึงสงสัยว่าทำไมคุณถึงตีความว่าไม่ตรงประเด็น ... โค้ดที่หายไปคือเมธอดว่างเปล่าที่ชื่อเมธอดน่าจะเป็นประโยชน์ (แต่ไม่ได้แสดงเพื่อความกะทัดรัด)
โทบี้

1

การค้นหาง่ายๆทำให้ฉันมีรหัสต่อไปนี้:

public class Deadlock {
    static class Friend {
        private final String name;
        public Friend(String name) {
            this.name = name;
        }
        public String getName() {
            return this.name;
        }
        public synchronized void bow(Friend bower) {
            System.out.format("%s: %s"
                + "  has bowed to me!%n", 
                this.name, bower.getName());
            bower.bowBack(this);
        }
        public synchronized void bowBack(Friend bower) {
            System.out.format("%s: %s"
                + " has bowed back to me!%n",
                this.name, bower.getName());
        }
    }

    public static void main(String[] args) {
        final Friend alphonse =
            new Friend("Alphonse");
        final Friend gaston =
            new Friend("Gaston");
        new Thread(new Runnable() {
            public void run() { alphonse.bow(gaston); }
        }).start();
        new Thread(new Runnable() {
            public void run() { gaston.bow(alphonse); }
        }).start();
    }
}

ที่มา: Deadlock


3
"มันมากมีแนวโน้มที่" ไม่ดีพอสำหรับ "ก็จะไปสู่การหยุดชะงัก"
alf

1

ต่อไปนี้เป็นตัวอย่างที่การล็อคการเก็บเธรดหนึ่งเริ่มต้นเธรดอื่นซึ่งต้องการการล็อกเดียวกันจากนั้นสตาร์ทเตอร์จะรอจนกว่าการเริ่มต้นจะเสร็จสิ้น ... ตลอดไป:

class OuterTask implements Runnable {
    private final Object lock;

    public OuterTask(Object lock) {
        this.lock = lock;
    }

    public void run() {
        System.out.println("Outer launched");
        System.out.println("Obtaining lock");
        synchronized (lock) {
            Thread inner = new Thread(new InnerTask(lock), "inner");
            inner.start();
            try {
                inner.join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

class InnerTask implements Runnable {
    private final Object lock;

    public InnerTask(Object lock) {
        this.lock = lock;
    }

    public void run() {
        System.out.println("Inner launched");
        System.out.println("Obtaining lock");
        synchronized (lock) {
            System.out.println("Obtained");
        }
    }
}

class Sample {
    public static void main(String[] args) throws InterruptedException {
        final Object outerLock = new Object();
        OuterTask outerTask = new OuterTask(outerLock);
        Thread outer = new Thread(outerTask, "outer");
        outer.start();
        outer.join();
    }
}

0

นี่คือตัวอย่าง:

สองเธรดกำลังทำงานโดยแต่ละเธรดกำลังรอให้อีกฝ่ายคลายล็อก

คลาสสาธารณะ ThreadClass ขยายเธรด {

String obj1,obj2;
ThreadClass(String obj1,String obj2){
    this.obj1=obj1;
    this.obj2=obj2;
    start();
}

public void run(){
    synchronized (obj1) {
        System.out.println("lock on "+obj1+" acquired");

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("waiting for "+obj2);
        synchronized (obj2) {
            System.out.println("lock on"+ obj2+" acquired");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }


}

}

การดำเนินการนี้จะนำไปสู่การหยุดชะงัก:

ชั้นเรียนสาธารณะ SureDeadlock {

public static void main(String[] args) {
    String obj1= new String("obj1");
    String obj2= new String("obj2");

    new ThreadClass(obj1,obj2);
    new ThreadClass(obj2,obj1);


}

}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.