2011-08-23 29 views
5

Tôi đang cố gắng viết chương trình rất đơn giản sẽ bắt chước DeadLock đơn giản, trong đó Thread A đợi Resource A bị khóa bởi Thread B và Thread B đợi Resource B bị khóa bởi Thread A.Java cho người mới - DeadLock giả

Đây là mã của tôi:

//it will be my Shared resource 
public class Account { 
    private float amount; 

    public void debit(double amount){ 
     this.amount-=amount; 
    } 

    public void credit(double amount){ 
     this.amount+=amount; 
    } 

} 

Đây là Runnable của tôi mà thực hiện hoạt động trên tài nguyên trên:

public class BankTransaction implements Runnable { 
    Account fromAccount,toAccount; 
    float ammount; 
    public BankTransaction(Account fromAccount, Account toAccount,float ammount){ 
     this.fromAccount = fromAccount; 
     this.toAccount = toAccount; 
     this.ammount = ammount; 
    } 

    private void transferMoney(){ 
     synchronized(fromAccount){ 
      synchronized(toAccount){ 
       fromAccount.debit(ammount); 
       toAccount.credit(ammount); 
       try { 
        Thread.sleep(500); 
       } catch (InterruptedException e) { 
       e.printStackTrace(); 
       } 
       System.out.println("Current Transaction Completed!!!"); 
      } 
     } 
    } 

    @Override 
    public void run() { 
     transferMoney(); 
    } 

} 

và cuối cùng là lớp học chính của tôi:

public static void main(String[] args) { 

    Account a = new Account(); 
    Account b = new Account(); 
    Thread thread1 = new Thread(new BankTransaction(a,b,500)); 

    Thread thread2 = new Thread(new BankTransaction(b,a,500)); 
     thread1.start(); 
     thread2.start(); 
System.out.println("Transactions Completed!!!"); 

    } 
} 

Tại sao mã này chạy thực thi thành công và tôi không có và deadLock?

Trả lời

10

Đã có tiềm năng cho bế tắc - nhưng cả hai khóa đều được mua lại nhanh chóng đến mức một chuỗi có thể nhận được cả hai trước khi có cơ hội để có được chuỗi đầu tiên.

Đặt khác Thread.sleep(500); cuộc gọi giữa hai báo cáo đồng bộ và nó không bế tắc: cả chủ đề sẽ bước vào khóa bên ngoài, giấc ngủ "của họ", sau đó khi họ thức dậy cả hai đều sẽ thấy rằng họ "bên trong" khóa đã mua.

Điều này là do thực tế là các câu lệnh được đồng bộ hóa là chống đối xứng: đối với một luồng, khóa được đồng bộ hóa bên ngoài là khóa bên trong cho luồng khác và cách khác.

+0

Giấc ngủ có thể giới thiệu tính không xác định. Bạn có thể làm cho nó xác định; xem câu trả lời của tôi dưới đây. – Toby

+0

@Toby: Có, bạn có thể làm cho nó xác định - nhưng 'ngủ' là cách tốt để hiển thị bế tắc * có thể xảy ra như thế nào, vì bạn luôn có thể tưởng tượng một chương trình không ngủ tương tự như chương trình * sử dụng chế độ ngủ, chỉ đơn giản là nhờ vào luồng không được lên lịch. Nói cách khác, một chương trình thất bại khi bạn chèn vào giấc ngủ vốn đã thiếu sót vì bạn có thể thấy cùng "vô tình" trong cuộc sống thực. Điều này cũng không đúng với việc chèn thông báo. –

5

Có thể một trong các chủ đề sẽ nhập cả hai phầnsynchronized, chặn toàn bộ chuỗi khác cho đến khi hoàn tất.

4

Bạn cần mô phỏng 'thời gian không may'. Thử thêm ngủ giữa hai khóa:

synchronized(fromAccount){ 
    Thread.sleep(2000); 
    synchronized(toAccount){ 
0

Lý do bế tắc là chủ đề A đang đợi B phát hành một số tài nguyên trước khi tiếp tục; giống với chuỗi B, nó sẽ không tiếp tục cho đến khi luồng A giải phóng một số tài nguyên. Nói cách khác, A và B chờ mãi mãi với nhau.

Trong đoạn mã, đồng bộ hóa có thể chặn các luồng khác trong khi vì chỉ có một luồng có thể thực thi khối tại thời điểm này. thread.sleep() tạm dừng chuỗi trong 500 mili giây, sau đó tiếp tục. Việc chờ đợi mãi mãi điều kiện lẫn nhau không thỏa mãn, rằng tại sao nó không phải là bế tắc.

Sau đoạn là một ví dụ tốt để minh họa bế tắc

public class threadTest{ 

    public class thread1 implements Runnable{ 
     private Thread _th2; 
     private int _foo; 

     public thread1(Thread th2){}; 
     public void run(){ 
     for(int i = 0; i<100; i++){foo += foo;}; 
     synchronized(this){this.notify()}; 
     synchronized(_th2){ 
      _th2.wait(); 
      _foo += _th2.foo; 
      System.out.print(" final result " + _foo); 
     } 
     } 
    } 

    public class thread2 implements Runnable{ 
     private final thread1 _th1; private int _foo; 
     public thread2(thread1 th1){}; 
     public void Run(){ 
      synchronized(_th1){_th1.wait()}; 
      synchronized(this){ 
      _foo += th1._foo(); 
      this.notify();     
      } 
     } 
    } 
    } 
} 

// chỉ cần bỏ qua các cách để truy cập biến private trong lớp

Bởi vì không có cơ chế bảo đảm trật tự thực hiện hai chủ đề, nó là rất có thể thread 2 sẽ không nhận được thông báo từ thread1 kể từ khi nó bắt đầu gần đây, do đó nó chờ thông báo trước khi tiếp tục thực hiện. Tương tự như thread1, nó không thể thực hiện tiếp theo cho đến khi nó nhận được thông báo từ thread2. cả hai người họ chờ đợi nhau mãi mãi, bế tắc điển hình.

2

Ngủ theo đề xuất của Jon ở trên có thể giới thiệu tính không xác định, bạn có thể làm cho nó xác định bằng cách sử dụng một số điều phối viên như chốt thay thế.Để làm rõ mặc dù, tôi đang nghĩ về nó như là một vấn đề thử nghiệm: làm thế nào để chứng minh một bế tắc mỗi lần và đó có thể không phải là những gì bạn đang tìm kiếm.

Xem điều này code để biết ví dụ và blog post mô tả một chút.

Các vấn đề liên quan