2013-05-12 9 views
33

Reentrancy có nghĩa là khóa được mua trên cơ sở cho mỗi luồng chứ không phải trên mỗi lần gọi.'Reentrancy' trong java

Vì khóa nội tại được giữ bởi một sợi, điều đó không có nghĩa là một chuỗi chạy một lần bằng cơ sở gọi?

Cảm ơn bạn, có vẻ như có nghĩa là: trong một thread, nếu tôi nhận được một khóa lockA khi chức năng quá trình doA mà gọi hàm doB, và doB cũng cần một khóa lockA, sau đó có thể là một wil reentrancy. Trong Java, hiện tượng này được mua trên mỗi sợi chỉ, vì vậy tôi không cần phải xem xét deadlocks?

+0

Hãy tưởng tượng nếu bước vào một phương pháp khóa đối tượng X, và trong khi ở phương pháp mà bạn gọi phương thức rất giống nhau (hoặc trực tiếp hoặc thêm vào trong cuộc gọi stack) và khóa đối tượng X một lần nữa. Chuyện gì xảy ra?Bạn có chờ đợi trên khóa bạn đã giữ, hoặc là nó nhận thức được rằng thread giữ nó cũng là thread gọi nó một lần nữa, và cho phép nó để vượt qua? Điều này được gọi là ủy nhiệm lại - bạn nhập cùng một phương thức mà bạn đã có trước đó. – Patashu

+1

Có, các khối và khóa được đồng bộ hóa trong Java được cấp lại, vì vậy khi chuỗi foo có khóa, nó có thể gọi một cách an toàn các phương thức cũng khóa thanh mà không phải mở khóa trước. – Patashu

+0

Bạn vẫn phải xem xét bế tắc, bế tắc có thể xảy ra khi hai luồng chờ nhau. – rubixibuc

Trả lời

52

reentrancy có nghĩa là ổ khóa được mua trên một sợi mỗi chứ không phải là cơ sở cho mỗi sự thỉnh nguyện.

Đó là định nghĩa gây hiểu lầm. Nó là đúng (loại), nhưng nó bỏ lỡ điểm thực.

Reentrancy có nghĩa là (nói chung thuật ngữ CS/IT) bạn làm điều gì đó, và trong khi bạn vẫn đang làm điều đó, bạn làm điều đó một lần nữa. Trong trường hợp ổ khóa nó có nghĩa là bạn làm điều gì đó như này trên một chủ đề duy nhất:

  1. Thu lấy chốt trên "foo".
  2. Làm điều gì đó
  3. Nhận khóa trên "foo". Lưu ý rằng chúng tôi đã không phát hành khóa mà chúng tôi đã mua trước đó.
  4. ...
  5. khóa phát hành vào "foo"
  6. ...
  7. khóa phát hành vào "foo"

Với một khóa reentrant/khóa cơ chế, nỗ lực để có được giống nhau khóa sẽ thành công, và sẽ tăng một bộ đếm nội bộ thuộc về khóa. Khóa sẽ chỉ được giải phóng khi người giữ khóa hiện tại đã nhả ra hai lần.

Dưới đây là một ví dụ trong Java sử dụng nguyên thủy ổ khóa đối tượng/màn hình ... mà là reentrant:

Object lock = new Object(); 
... 
synchronized (lock) { 
    ... 
    doSomething(lock, ...) 
    ... 
} 

public void doSomething(Object lock, ...) { 
    synchronized (lock) { 
     ... 
    } 
} 

Các thay thế cho reentrant là khóa không reentrant, nơi mà nó sẽ là một lỗi cho một thread để cố gắng để có được một khóa mà nó đã nắm giữ.

Lợi thế của việc sử dụng khóa reentrant là bạn không phải lo lắng về khả năng thất bại do vô tình có được một khóa mà bạn đã giữ. Nhược điểm là bạn không thể giả định rằng không có gì bạn gọi sẽ thay đổi trạng thái của các biến khóa được thiết kế để bảo vệ. Tuy nhiên, đó không phải là vấn đề thường gặp.Khóa thường được sử dụng để bảo vệ chống lại các thay đổi trạng thái đồng thời được thực hiện bởi các chủ đề khác.


Vì vậy, tôi không cần xem xét sự bế tắc?

Có bạn đã làm.

Chủ đề sẽ không bế tắc đối với chính nó (nếu khóa là reentrant). Tuy nhiên, bạn có thể bị bế tắc nếu có các luồng khác có thể có khóa trên đối tượng bạn đang cố khóa.

+0

Vì một luồng là một khi quá trình chạy, làm thế nào tôi có thể trong khi vẫn làm việc đó, sau đó làm lại? – znlyj

+1

Xem mã Java mẫu mà tôi vừa thêm vào. –

+0

Có, Cảm ơn bạn. – znlyj

1

Điều này chỉ có nghĩa là khi chuỗi có khóa, nó có thể nhập phần mã bị khóa bao nhiêu lần tùy theo nhu cầu. Vì vậy, nếu bạn có một phần mã được đồng bộ hóa như một phương thức, chỉ chuỗi đã đạt được khóa mới có thể gọi phương thức đó, nhưng có thể gọi phương thức đó bao nhiêu lần tùy thích, bao gồm bất kỳ mã nào khác được giữ bởi cùng một khóa. Điều này quan trọng nếu bạn có một phương thức gọi một phương thức khác và cả hai đều được đồng bộ hóa bằng cùng một khóa. Nếu đây không phải là trường hợp. Cuộc gọi phương thức thứ hai sẽ chặn. Nó cũng sẽ áp dụng cho các cuộc gọi phương thức đệ quy.

public void methodA() 
{ 
    // other code 
    synchronized(this) 
    { 
      methodB(); 
    } 
} 

public void methodB() 
{ 
    // other code 
    syncrhonized(this) 
    { 
      // it can still enter this code  
    } 

} 
13

Hãy tưởng tượng một cái gì đó như thế này:

function A(): 
    lock (X) 
     B() 
    unlock (X) 

function B(): 
    A() 

Bây giờ chúng ta gọi là A. Sau đây sẽ xảy ra:

  • Chúng tôi nhập A, khóa X
  • Chúng tôi nhập B
  • Chúng tôi nhập A một lần nữa , khóa X lại một lần nữa

Vì w e chưa bao giờ thoát khỏi lời gọi đầu tiên của A, X vẫn bị khóa. Điều này được gọi là tái nhập - trong khi hàm A chưa được trả về, hàm A được gọi lại. Nếu A dựa vào một số trạng thái tĩnh toàn cầu, điều này có thể gây ra lỗi 'tái nhập cảnh', nơi trước khi trạng thái tĩnh được làm sạch từ lối thoát của hàm, hàm sẽ chạy lại và các giá trị được tính một nửa va chạm với đầu cuộc gọi thứ hai.

Trong trường hợp này, chúng tôi chạy vào một khóa mà chúng tôi đang nắm giữ. Nếu khóa được tái nhập cảnh nhận thức, nó sẽ nhận ra chúng tôi là cùng một thread giữ khóa đã và cho chúng tôi thông qua. Nếu không, nó sẽ bế tắc mãi mãi - nó sẽ chờ một khóa nó đã giữ.

Trong java, locksynchronized là nhận thức lại - nếu khóa được giữ bởi một sợi và chuỗi cố gắng lấy lại cùng một khóa, nó được cho phép. Vì vậy, nếu chúng tôi đã viết mã giả ở trên trong Java, nó sẽ không bế tắc.

7

Java đồng thời ở các bang cuốn sách thực hành - Reentrancy means that locks are acquired on a per-thread rather than per-invocation basis.

Hãy để tôi giải thích những gì nó chính xác nghĩa. Đầu tiên của tất cả các khóa nội tại được reentrant của thiên nhiên. Cách tái hiện là đạt được bằng cách duy trì một truy cập cho số lượng ổ khóa mua lại và chủ sở hữu của khóa. Nếu số đếm là 0 và không có chủ sở hữu nào được liên kết với nó, nghĩa là khóa không được giữ bởi bất kỳ chuỗi nào. Khi một luồng nhận được khóa, JVM sẽ ghi lại chủ sở hữu và đặt bộ đếm thành 1.Nếu cùng một luồng cố gắng lấy lại khóa, bộ đếm được tăng lên và khi chuỗi chủ sở hữu tồn tại bộ đếm khối đồng bộ bị giảm. Khi đếm đến 0 một lần nữa khóa được phát hành.

Một ví dụ đơn giản sẽ là -

public class Test { 
    public synchronized void performTest() { 
     //... 
    } 
} 

public class CustomTest extends Test { 
    public synchronized void performTest() { 
     //... 
     super.performTest(); 
    } 
} 

mà không reentrancy sẽ có một bế tắc.

enter image description here