2009-10-13 61 views
11

Tôi đang tìm hiểu về sự bế tắc trong Java, và có này mẫu mã từ hướng dẫn chính thức của Sun:Câu hỏi Về Deadlock Tình hình trong Java

Alphonse và Gaston là bạn bè, và tín lớn trong lịch sự. Quy tắc lịch sự nghiêm ngặt là khi bạn cung cấp cho một người bạn, bạn vẫn phải cúi đầu cho đến khi bạn của bạn có cơ hội để trả lại cây cung. Rất tiếc, quy tắc này không tính đến khả năng mà hai người bạn có thể cung cấp cho nhau cùng một lúc.

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

Dưới đây là lời giải thích của Sun:

Khi bế tắc chạy, nó là vô cùng khả năng là cả hai chủ đề sẽ chặn khi họ cố gắng để gọi bowBack. Không chặn nào sẽ kết thúc, bởi vì mỗi luồng đang đợi khác để thoát khỏi cung.

Tôi dường như không theo kịp. Khi alphonse.bow (gaston) chạy, phương pháp cung bị khóa. Vì vậy, bây giờ nó sẽ lần đầu tiên in "Gaston đã cúi chào tôi!" Sau đó, nó sẽ tiếp tục và gọi bowback, và khóa bowBack là tốt. Làm thế nào điều này có thể gây ra bế tắc? Tôi có hiểu lầm điều gì sẽ xảy ra khi một phương thức đồng bộ được gọi?

Nếu ai đó có thể cho tôi lời giải thích dễ dàng, cảm ơn.

+0

Hình như có rất nhiều câu trả lời đúng ở đây, nhưng tôi nghĩ rằng nó có thể hữu ích hơn khi định dạng phản hồi thành 2 cột riêng biệt, mỗi cột đại diện cho một chuỗi khác nhau. Điều này thực sự sẽ lái xe về nhà thực tế là 2 bộ hướng dẫn có thể chạy đồng thời. –

+1

bản sao có thể có của [Đang cố gắng bọc bộ não của tôi xung quanh cách bế tắc khóa] (http://stackoverflow.com/questions/749641/trying-to-wrap-my-wee-brain-around-how-threads-deadlock) –

+0

Tôi có sự nhầm lẫn tương tự. Cảm ơn bài đăng và câu trả lời. –

Trả lời

27

Một điểm quan trọng cần lưu ý là nó không phải là phương pháp được khóa nhưng trường hợp đối tượng.

Khi bạn gọi alphonse.bow(gaston), nó cố gắng lấy khóa trên alphonse. Một khi nó có khóa, nó in một tin nhắn, sau đó gọi gaston.bowBack(alphonse). Tại thời điểm này, nó cố gắng để có được khóa trên gaston. Một khi nó có khóa, nó in một tin nhắn, sau đó giải phóng khóa, và cuối cùng là khóa trên alphonse được phát hành.

Trong bế tắc, khóa được mua theo thứ tự như vậy mà không có cách nào cho một trong hai luồng tiếp tục.

  • Chủ đề 1: mua lại khóa trên alphonse
  • Chủ đề 2: mua lại khóa trên gaston
  • Chủ đề 1: in thông điệp
  • Chủ đề 1: cố gắng để có được khóa trên gaston - không thể, bởi vì Chủ đề 2 đã có nó.
  • Chủ đề 2: in thông báo
  • Chủ đề 2: cố gắng lấy khóa trên alphonse - không thể, vì Chủ đề 1 đã có nó.
+0

Ah. Ok, tôi nghĩ tôi hiểu rồi. Vì vậy, khi bạn thêm từ khóa đồng bộ vào trước phương thức, điều đó có nghĩa là khi một chuỗi gọi phương thức này, toàn bộ đối tượng "khóa", nghĩa là không có chủ đề nào khác có thể gọi bất kỳ phương thức nào khác trên đối tượng này, ngay cả khi những phương thức KHÁC đó chỉ in "xin chào" ? – Saobi

+2

Không hoàn toàn. Không có luồng nào có thể gọi bất kỳ phương thức được đồng bộ nào khác trên đối tượng này và không có luồng nào có thể nhập một khối {} được đồng bộ hóa trên cá thể đối tượng này. –

+0

ví dụ: các chủ đề khác có thể gọi alphonse.toString() hoặc alphonse.getName() ngay cả khi một chuỗi khác có khóa trên alphonse. –

-3

Tôi phải kiểm tra kỹ, nhưng tôi nghĩ rằng một phương pháp đồng bộ sẽ khóa trên đối tượng lớp, vì vậy nó khóa các phương thức đồng bộ khác trong cùng một lớp.

Tôi nghĩ nó tự khóa trên đối tượng lớp, vì vậy nó thậm chí còn chặn các phiên bản khác nhau.

Chỉnh sửa để thêm:

Hãy nhìn vào phần này của java language spec

Mỗi phương pháp cung lấy nó đối tượng riêng theo dõi. Cả hai sau đó cố gắng gọi trở lại của đối tượng khác, và khối chờ đợi cho màn hình khác.

+4

Không, đồng bộ trong một chữ ký phương thức không tĩnh sẽ chỉ khóa phiên bản hiện tại, không phải đối tượng lớp. –

+0

Tìm thấy phần thích hợp của thông số kỹ thuật. Bạn đúng. – developmentalinsanity

+0

Ngẫu nhiên, khóa đối tượng lớp sẽ thực sự ngăn chặn bế tắc trong trường hợp này! –

8

alphonsegaston là hai đối tượng khác nhau. Mỗi đối tượng có một màn hình nội tại (khóa) được liên kết với nó.

Điều này có thể xảy ra như sau:

alphonse được tạo. Màn hình đối tượng của anh ta là 1.

gaston được tạo ra. Giám sát đối tượng của anh ấy là 2.

alphonse.bow (gaston); alphonse hiện đang sở hữu khóa # 1

gaston.cung (alphonse); Gaston hiện đang sở hữu khóa # 2

Alphonse gọi bowBack trên Gaston và đang chờ khóa # 2 Gaston gọi bowBack trên Alphonse và đang chờ khóa # 1

Make ý nghĩa? Sử dụng khóa từ khóa được đồng bộ hóa mà các cá thể giám sát trong suốt thời gian của phương thức. Ví dụ có thể được viết lại như 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 void bow(Friend bower) { 
      synchronized(this) {    
         System.out.format("%s: %s has bowed to me!%n", 
        this.name, bower.getName()); 
         bower.bowBack(this); 
      } 
     } 
     public void bowBack(Friend bower) { 
      synchronized(this) { 
         System.out.format("%s: %s has bowed back to me!%n", 
        this.name, bower.getName()); 
      } 
     } 
    } 
} 
+0

Khi alphonse gọi bowBack, nó sẽ chuyển thể hiện của chính nó (bowBack (this)). Tại sao bowback lại yêu cầu khóa dạ dày? – Saobi

+0

Nó không, nó chỉ cho mục đích khai thác gỗ. Nó có thể dễ dàng thông qua friend.getName() thay vì toàn bộ đối tượng Friend. – Kevin

1

Khóa được giữ trên các đối tượng Java chứ không phải các phương thức java. Vì vậy, khi đồng bộ được sử dụng trên một phương pháp, nó khóa đối tượng "này". Trong trường hợp của một phương thức tĩnh, nó khóa đối tượng lớp.

Bạn có thể xác định rõ ràng đối tượng màn hình bằng cách sử dụng synchronized (object) { }

0

đồng bộ trong định nghĩa phương thức là viết tắt để đồng bộ hóa chính đối tượng (chính) này. Về bản chất, điều này có nghĩa là bow() và bowBack() không thể được gọi cùng một đối tượng Friend cùng một lúc.

Bây giờ nếu cả hai Bạn bè vào trong cung(), không ai trong số họ có thể gọi phương thức bowBack() của nhau.

1

Để thêm vào simonn, et al.,

Nếu bạn muốn hình dung này, biên dịch và chạy chương trình và tạo ra một bãi chứa thread của chương trình đang chạy. Bạn có thể thực hiện việc này bằng cách nhập Ctrl - Break vào bảng điều khiển Windows hoặc đưa ra lệnh kill -QUIT [pid] vào hệ thống * nix. Điều này sẽ cung cấp cho bạn danh sách tất cả các Threads đang chạy trong hệ thống của bạn và nơi chúng đang chạy hoặc đang chờ, cũng như màn hình các chủ đề đang khóa hoặc chờ để khóa.

Nếu bạn thay đổi tên chủ đề của bạn trong nhà thầu của họ, bạn sẽ có một thời gian dễ dàng hơn việc tìm kiếm chúng trong các chủ đề đầy đủ bãi:

new Thread(new Runnable() { 
     public void run() { alphonse.bow(gaston); } 
    }, "Alphonse").start(); 
    new Thread(new Runnable() { 
     public void run() { gaston.bow(alphonse); } 
    }, "Gaston").start();