Viết một chương trình chắc chắn sẽ đi vào bế tắc [đóng]


86

Gần đây tôi đã nhận được câu hỏi này trong một cuộc phỏng vấn.

Tôi đã trả lời rằng deadlock xảy ra nếu việc xen kẽ gặp sự cố, nhưng người phỏng vấn nhấn mạnh rằng một chương trình sẽ luôn đi vào bế tắc bất kể xen kẽ có thể được viết.

Chúng ta có thể viết một chương trình như vậy không? Bạn có thể chỉ cho tôi một số chương trình ví dụ như vậy không?


3
Người phỏng vấn chắc chắn là một kẻ ngu ngốc.
Lion

23
Người phỏng vấn chắc chắn không phải là một kẻ ngu ngốc. Hiểu biết đầy đủ về một chủ đề có nghĩa là bạn sẽ có thể giải thích các trường hợp cạnh cực: tạo chương trình để không bao giờ khóa và luôn khóa.
Yuriy Zubarev

Câu trả lời:


100

CẬP NHẬT: Câu hỏi này là chủ đề của blog của tôi vào tháng 1 năm 2013 . Cảm ơn vì câu hỏi tuyệt vời của bạn!


Làm thế nào chúng ta có thể viết một chương trình luôn đi vào bế tắc bất kể các luồng được lên lịch như thế nào?

Đây là một ví dụ trong C #. Lưu ý rằng chương trình dường như không chứa ổ khóa và không có dữ liệu được chia sẻ. Nó chỉ có một biến cục bộ duy nhất và ba câu lệnh, nhưng nó vẫn hoàn toàn chắc chắn 100%. Người ta sẽ khó nghĩ ra một chương trình đơn giản hơn nhưng chắc chắn là bế tắc.

Bài tập cho người đọc số 1: giải thích sự bế tắc này như thế nào. (Một câu trả lời là trong các bình luận.)

Bài tập cho người đọc # 2: chứng minh cùng một bế tắc trong Java. (Câu trả lời là ở đây: 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
Kiến thức của tôi về C # lý thuyết còn hạn chế, nhưng tôi giả sử rằng trình nạp lớp đảm bảo mã được chạy đơn luồng như trong Java. Tôi khá chắc rằng có một ví dụ tương tự trong Java Puzzlers.
Voo

11
@Voo: Bạn có một trí nhớ tốt. Neal Gafter - đồng tác giả của "Java Puzzlers" - và tôi đã trình bày một phiên bản khá khó hiểu hơn của mã này trong bài nói chuyện "C # Puzzlers" của chúng tôi tại Hội nghị nhà phát triển Oslo một vài năm trước.
Eric Lippert

41
@Lieven: Hàm khởi tạo tĩnh phải chạy không quá một lần và nó phải chạy trước lần gọi đầu tiên đến bất kỳ phương thức tĩnh nào trong lớp. Main là một phương thức tĩnh, vì vậy luồng chính gọi ctor tĩnh. Để đảm bảo nó chỉ chạy một lần, CLR lấy ra một khóa không được giải phóng cho đến khi ctor tĩnh kết thúc. Khi ctor bắt đầu một luồng mới, luồng đó cũng gọi một phương thức tĩnh, vì vậy CLR cố gắng lấy khóa để xem liệu nó có cần chạy ctor hay không. Chuỗi chính trong khi đó "tham gia" chuỗi bị chặn và bây giờ chúng ta có bế tắc.
Eric Lippert

33
@artbristol: Tôi chưa bao giờ viết nhiều như vậy một dòng mã Java; Tôi thấy không có lý do gì để bắt đầu ngay bây giờ.
Eric Lippert

4
Ồ, tôi cho rằng bạn đã có câu trả lời cho Bài tập số 2 của mình. Chúc mừng bạn đã nhận được nhiều lượt ủng hộ vì đã trả lời một câu hỏi Java.
artbristol

27

Chốt ở đây đảm bảo rằng cả hai khóa đều được giữ khi mỗi sợi cố gắng khóa sợi kia:

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

   }

}

Thật thú vị khi chạy jconsole, nó sẽ hiển thị chính xác cho bạn những bế tắc trong tab Threads.


3
Đó là điều tốt nhất cho đến nay, nhưng tôi sẽ thay thế sleepbằng một chốt thích hợp: về mặt lý thuyết, chúng tôi có một điều kiện đua ở đây. Mặc dù chúng tôi gần như có thể chắc chắn 0,5 giây là đủ, nhưng nó không quá tốt cho một nhiệm vụ phỏng vấn.
Alf

25

Bế tắc xảy ra khi các luồng (hoặc bất cứ thứ gì mà nền tảng của bạn gọi là đơn vị thực thi của nó) có được tài nguyên, trong đó mỗi tài nguyên chỉ có thể được giữ bởi một luồng tại một thời điểm và giữ các tài nguyên đó theo cách mà không thể sử dụng trước các khoản lưu giữ và tồn tại một số mối quan hệ "vòng tròn" giữa các luồng sao cho mỗi luồng trong deadlock đang chờ lấy một số tài nguyên do một luồng khác nắm giữ.

Vì vậy, một cách dễ dàng để tránh bế tắc là đặt một số thứ tự tổng thể cho tài nguyên và áp đặt quy tắc rằng tài nguyên chỉ được thu nhận bởi các luồng theo thứ tự . Ngược lại, một bế tắc có thể được cố ý tạo ra bằng cách chạy các luồng thu nhận tài nguyên, nhưng không thu được chúng theo thứ tự. Ví dụ:

Hai sợi chỉ, hai ổ khóa. Luồng đầu tiên chạy một vòng lặp cố gắng lấy các khóa theo một thứ tự nhất định, luồng thứ hai chạy một vòng lặp cố gắng lấy các khóa theo thứ tự ngược lại. Mỗi luồng sẽ giải phóng cả hai khóa sau khi có được các khóa thành công.

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

Bây giờ, đã có một vài bình luận trong câu hỏi này chỉ ra sự khác biệt giữa khả năng xảy ra và sự chắc chắn của bế tắc. Theo một nghĩa nào đó, sự khác biệt là một vấn đề học thuật. Từ quan điểm thực tế, tôi chắc chắn muốn thấy một hệ thống đang chạy không bị bế tắc với mã mà tôi đã viết ở trên :)

Tuy nhiên, các câu hỏi phỏng vấn đôi khi có thể mang tính học thuật, và câu hỏi SO này có từ "chắc chắn" trong tiêu đề, vì vậy những gì tiếp theo là một chương trình chắc chắn bế tắc. Hai Lockerđối tượng được tạo, mỗi đối tượng được cung cấp hai khóa và một CountDownLatchđược sử dụng để đồng bộ hóa giữa các luồng. Mỗi Lockerchốt khóa đầu tiên sau đó đếm ngược chốt một lần. Khi cả hai luồng đã có được một khóa và đếm ngược chốt, chúng tiếp tục vượt qua rào cản chốt và cố gắng có được khóa thứ hai, nhưng trong mỗi trường hợp, luồng kia đã giữ khóa mong muốn. Tình trạng này dẫn đến một sự bế tắc nhất định .

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
Xin lỗi vì đã trích dẫn Linus, "Nói chuyện thật rẻ. Hãy cho tôi xem mã." - đó là một nhiệm vụ tốt, và nó khó hơn một cách đáng ngạc nhiên.
alf

2
Nó có thể chạy mã này mà không bế tắc
Vladimir Zhilyaev

1
Ok, các bạn tàn bạo, nhưng tôi nghĩ đây là một câu trả lời đầy đủ.
Greg Mattes

@GregMattes nhờ :) Không thể thêm bất cứ điều gì nhưng +1, và hy vọng bạn có vui vẻ :)
Alf

15

Đây là một ví dụ Java bằng cách làm theo một ví dụ của 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
Tôi nghĩ rằng việc sử dụng phương pháp tham gia chạy ít gây hiểu lầm. Nó gợi ý rằng tham gia khác này ngoài tham gia trong khối tĩnh là cần thiết để có được một bế tắc trong khi bế tắc được gây ra bởi câu lệnh "new Lock ()". Viết lại của tôi, sử dụng phương pháp tĩnh như trong C # Ví dụ: stackoverflow.com/a/16203272/2098232
luke657

Bạn có thể giải thích ví dụ của bạn?
gstackoverflow

theo các thí nghiệm của tôi t.join (); bên trong run () phương pháp là không cần thiết
gstackoverflow

Tôi đã gỡ bỏ mã dư thừa, giúp ngăn chặn sự hiểu biết
gstackoverflow

11

Đây là một ví dụ từ tài liệu:

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 Hướng dẫn liên kết java.
mre

4
"nó rất có khả năng" là không đủ tốt cho "chắc chắn sẽ đi vào bế tắc"
Alf

1
@alf Ồ nhưng vấn đề cơ bản được thể hiện khá độc đáo ở đây. Người ta có thể viết một bộ lập lịch Round robin hiển thị một Object invokeAndWait(Callable task)phương pháp. Sau đó, tất cả Callable t1đã làm là invokeAndWait()cho Callable t2trong thời gian cuộc sống của mình trước khi trở về, và ngược lại.
user268396

2
@ user268396 à, vấn đề cơ bản là tầm thường và nhàm chán :) Toàn bộ điểm của nhiệm vụ là tìm hiểu — hoặc chứng minh rằng bạn hiểu — điều đáng ngạc nhiên là rất khó để có được một deadlock được đảm bảo (cũng như để đảm bảo bất cứ điều gì trong một thế giới không đồng bộ ).
alf

4
@bezz sleepchán quá. Mặc dù tôi tin rằng sẽ không có luồng nào bắt đầu trong 5 giây, nhưng dù sao thì đó cũng là một điều kiện của cuộc đua. Bạn không muốn thuê một lập trình viên sẽ dựa vào sleep()trong việc giải quyết điều kiện chủng tộc :)
Alf

9

Tôi đã viết lại phiên bản Java của Yuriy Zubarev về ví dụ deadlock được đăng bởi Eric Lippert: https://stackoverflow.com/a/9286697/2098232 để gần giống với phiên bản C # hơn. Nếu khối khởi tạo của Java hoạt động tương tự như phương thức khởi tạo tĩnh của C # và lần đầu tiên có được khóa, chúng ta không cần một luồng khác cũng gọi phương thức nối để có được khóa chết, nó chỉ cần gọi một số phương thức tĩnh từ lớp Khóa, như C # ban đầu thí dụ. Kết quả bế tắc dường như xác nhận điều này.

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

}

Tại sao khi tôi nhận xét Lock.initialize () trong phương thức chạy nó không bị bế tắc? phương thức khởi tạo không làm gì cả ??
Aequitas

@Aequitas chỉ là phỏng đoán nhưng phương pháp có thể đang được tối ưu hóa; không chắc chắn về cách mà sẽ làm việc với chủ đề
Dave Cousineau

5

Đây không phải là một nhiệm vụ phỏng vấn đơn giản nhất mà bạn có thể nhận được: trong dự án của tôi, nó đã làm tê liệt công việc của một nhóm trong cả ngày. Rất dễ dàng để làm cho chương trình của bạn dừng lại, nhưng rất khó để đưa nó đến trạng thái mà kết xuất luồng viết một cái gì đó như,

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)

Vì vậy, mục tiêu sẽ là để có được một bế tắc mà JVM sẽ coi là một bế tắc. Rõ ràng, không có giải pháp nào như

synchronized (this) {
    wait();
}

sẽ hoạt động theo nghĩa đó, mặc dù chúng thực sự sẽ dừng lại mãi mãi. Dựa vào điều kiện cuộc đua cũng không phải là một ý kiến ​​hay, vì trong cuộc phỏng vấn, bạn thường muốn thể hiện một thứ gì đó có hiệu quả rõ ràng, chứ không phải thứ gì đó nên hoạt động trong hầu hết thời gian.

Bây giờ, sleep()giải pháp là ổn theo một nghĩa nào đó, thật khó để tưởng tượng một tình huống mà nó không hoạt động, nhưng không công bằng (chúng ta đang ở trong một môn thể thao công bằng, phải không?). Giải pháp của @artbristol (của tôi cũng giống nhau, chỉ là các đối tượng khác nhau làm màn hình) rất hay, nhưng dài và sử dụng các nguyên thủy đồng thời mới để đưa các luồng ở trạng thái phù hợp, điều này không thú vị lắm:

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) {
            }
        }
    }
}

Tôi nhớ lại rằng synchronizedgiải pháp -chỉ phù hợp với 11..13 dòng mã (không bao gồm nhận xét và nhập khẩu), nhưng vẫn chưa nhớ lại thủ thuật thực sự. Sẽ cập nhật nếu tôi làm.

Cập nhật: đây là một giải pháp xấu cho 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) {
            }
        }
    }
}

Lưu ý rằng chúng tôi thay thế một chốt bằng một màn hình đối tượng (sử dụng "a"như một đối tượng).


Hum Tôi nghĩ đó là một nhiệm vụ phỏng vấn công bằng. Nó yêu cầu bạn thực sự hiểu các deadlock và khóa trong Java. Tôi không nghĩ rằng ý tưởng chung cũng khó đến vậy (đảm bảo rằng cả hai chủ đề chỉ có thể tiếp tục sau khi cả hai đã khóa tài nguyên đầu tiên của họ), bạn chỉ nên nhớ CountdownLatch - nhưng với tư cách là người phỏng vấn, tôi sẽ giúp người được phỏng vấn ở phần đó nếu anh ta có thể giải thích chính xác những gì anh ta cần (đó không phải là lớp học mà hầu hết các nhà phát triển cần và bạn không thể tìm kiếm nó trong một cuộc phỏng vấn). Tôi rất thích nhận được những câu hỏi thú vị như vậy cho các cuộc phỏng vấn!
Voo

@Voo tại thời điểm chúng tôi đang chơi với nó, không có chốt trong JDK, vì vậy tất cả đều bằng tay. Và sự khác biệt giữa LOCKEDwaiting to locklà tinh tế, không phải thứ bạn đọc trong bữa ăn sáng. Nhưng tốt, bạn có thể đúng. Hãy để tôi diễn đạt lại.
alf

4

Phiên bản C # này, tôi đoán java sẽ khá giống nhau.

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

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

2
Tốt! Thực sự là chương trình C # ngắn nhất để tạo ra một deadlock nếu bạn loại bỏ các consolecâu lệnh. Bạn có thể chỉ cần viết toàn bộ Mainhàm dưới dạng 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();
        }
    }
}

Chương trình luôn bị bế tắc bởi vì mỗi luồng đang đợi ở rào cản cho các luồng khác, nhưng để chờ đợi rào cản, luồng đó phải giữ màn hình trên BadRunnable.class.


3
} catch (InterruptedException ex) { continue; }... đẹp
artbristol

2

Có một ví dụ trong Java ở đây

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

Trường hợp kẻ bắt cóc rơi vào bế tắc khi anh ta từ chối từ bỏ nạn nhân cho đến khi anh ta nhận được tiền mặt nhưng người thương lượng từ chối đưa tiền mặt cho đến khi anh ta lấy được nạn nhân.


Việc triển khai đó không phù hợp như đã cho. Một số đoạn mã dường như bị thiếu. Tuy nhiên, ý kiến ​​chung mà bạn trình bày là đúng khi có liên quan đến tranh chấp tài nguyên dẫn đến bế tắc.
Master Chief

ví dụ là sư phạm nên tôi tò mò tại sao bạn giải thích nó như là không thích hợp ... mã mất tích là phương pháp trống nơi tên phương pháp được cho là hữu ích (nhưng không được hiển thị cho ngắn gọn)
Toby

1

Một tìm kiếm đơn giản đã cho tôi mã sau:

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

Nguồn: Bế tắc


3
"nó rất có khả năng" là không đủ tốt cho "chắc chắn sẽ đi vào bế tắc"
Alf

1

Đây là mẫu trong đó một khóa giữ luồng bắt đầu một luồng khác muốn có cùng một khóa và sau đó trình khởi động đợi cho đến khi bắt đầu kết thúc ... mãi mãi:

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

Đây là một ví dụ:

hai chủ đề đang chạy, mỗi chủ đề chờ người khác giải phóng khóa

public class ThreadClass mở rộng Thread {

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

    }


}

}

Chạy điều này sẽ dẫn đến bế tắc:

lớp công khai 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);


}

}

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.