2013-04-26 14 views
6

Tôi đã có một chủ đề Java làm điều gì đó như thế này:Java chủ đề một cách tự nhiên thức dậy

while (running) { 
    synchronized (lock) { 
     if (nextVal == null) { 
      try { 
       lock.wait(); 
      } catch (InterruptedException ie) { 
       continue; 
      } 
     } 

     val = nextVal; 
     nextVal = null; 
    } 
    ...do stuff with 'val'... 
} 

Ở những nơi khác tôi thiết lập giá trị như thế này:

if (val == null) { 
    LOG.error("null value"); 
} else { 
    synchronized (lock) { 
     nextVal = newVal; 
     lock.notify(); 
    } 
} 

thoảng (theo nghĩa đen một lần mỗi vài triệu times) nextVal sẽ được đặt thành null. Tôi đã ném trong các tin nhắn đăng nhập và tôi có thể thấy rằng tự thực hiện như sau:

  1. thread1 đặt NEXTVAL để newVal
  2. thread1 gọi lock.notify()
  3. thread2 tỉnh dậy từ khóa. chờ()
  4. thread2 đặt val để NEXTVAL
  5. thread2 đặt NEXTVAL null
  6. thread2 hiện công cụ với val
  7. thread2 gọi lock.wait()
  8. thread2 tỉnh dậy từ lock.wait()
    • không có thread khác đã kêu gọi lock.notify() và thread2 đã không bị gián đoạn
  9. thread2 đặt val để NEXTVAL (đó là null)
  10. v.v.

Tôi đã kiểm tra rõ ràng và khóa đang thức dậy lần thứ hai, nó không bị gián đoạn.

Tôi có làm gì sai ở đây không?

+0

+1 cho lời giải thích rõ ràng, nhưng -1 vì không đọc Javadoc cẩn thận hơn :-) –

+0

IIRC, không đảm bảo rằng chuỗi sẽ không đánh thức khi chờ trong khi điều kiện chờ vẫn không hài lòng. Bạn phải luôn luôn kiểm tra điều kiện và thực hiện lại sự chờ đợi nếu không hài lòng. –

+0

(Lý do cho việc này liên quan đến sự phức tạp của việc thực hiện logic "đánh thức". Đảm bảo rằng không bao giờ có một lần đánh thức giả mạo là không thể nếu không có nhiều đồng bộ hóa đắt tiền.) –

Trả lời

3

Yup, Thread s tự động thức dậy. Điều này được tuyên bố rõ ràng trong the Javadoc:

"Chủ đề cũng có thể thức dậy mà không được thông báo, gián đoạn hoặc hết thời gian, được gọi là đánh thức giả mạo".

Bạn cần phải wait trong một vòng lặp. Điều này cũng được đề cập một cách rõ ràng trong javadoc:

synchronized (obj) { 
    while (<condition does not hold>) 
     obj.wait(timeout); 
    ... // Perform action appropriate to condition 
} 

Trong trường hợp của bạn:

while (running) { 
    synchronized (lock) { 
     while (nextVal == null) { 
      try { 
       lock.wait(); 
      } catch (InterruptedException ie) { 
       //oh well 
      } 
     } 

     val = nextVal; 
     nextVal = null; 
    } 
    ...do stuff with 'val'... 
} 
+0

Tôi nên lưu ý rằng giải pháp 'while (nextVal == null) {' sẽ không hoạt động đối với tôi vì tôi bỏ qua chi tiết rằng chuỗi này bị dừng qua một phương thức: đồng bộ (khóa) { running = true ; lock.notify(); } vì vậy tôi không thể quay 'nextVal == null'.Thay vào đó, tôi đã thêm: nếu (nextVal == null) tiếp tục; trước 'val = nextVal' để đảm bảo rằng 'đang chạy' được kiểm tra trên mỗi chuyến đi qua vòng lặp. – dglo

+1

Loại bỏ điều đó một cách phù hợp, biến nó thành 'while (true)'. Kiểm tra cờ 'running' ở một nơi khác và' return'. Bạn thậm chí có thể thêm một số logic trong ngắt sau đó bạn có thể 'ngắt '' Thread' để dừng lại. –

+0

Có, ngắt là cách đúng để dừng một chuỗi đang chạy. Sử dụng một cách tiếp cận cờ 'boolean' riêng biệt đầy những cạm bẫy. Giống như, tôi hy vọng 'chạy' được đánh dấu 'dễ bay hơi' ở đây! –

1

giả up trỗi dậy là khá phổ biến và vì thế nó luôn luôn nên wait() trên một điều kiện trong vòng một vòng lặp.

Thay đổi mã của bạn như sau:

while (nextVal == null) { 
    try { 
     lock.wait(); 
    } catch (InterruptedException ignored) { 
    } 
} 

Cụ thể cho các mã bạn đã chia sẻ: while cũng giúp bạn tránh được những chi phí không cần thiết của phát hành và tái mua khóa tương tự khi mã của bạn chạm continue;

Tài liệu tham khảo:
http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Object.html#wait()

Từ public final void wait() tài liệu:
... ngắt và wakeups giả mạo là có thể, và phương pháp này nên luôn được sử dụng trong một vòng lặp:

synchronized (obj) { 
    while (<condition does not hold>) 
     obj.wait(); 
    ... // Perform action appropriate to condition 
} 
1

Đây có thể là một wakeup giả mạo, nhưng đó không phải là nguyên nhân duy nhất có thể . Nó chắc chắn là một vấn đề với logic của bạn. Bạn cần phải chờ đợi bên trong một vòng lặp mà kiểm tra lại điều kiện.

Khi một chủ đề đánh thức từ khi chờ, nó không còn khóa nữa. Nó phát hành khóa khi nó bắt đầu chờ đợi, nó cần phải phản ứng lại khóa trước khi nó có thể tiến hành. Chuỗi chỉ được đánh thức thường có thể là tiếp theo trong dòng do mối quan hệ luồng (có thể là lý do tại sao mã của bạn hoạt động phần lớn thời gian), nhưng vẫn có cơ hội không phải là nó; thread khác có thể đi vào và khóa khóa, làm điều của nó và để lại null nextVal, trước khi chủ đề đánh thức có thể lấy khóa. Điều đó có nghĩa là thử nghiệm cho null mà luồng được tạo trước khi chờ đợi không còn phù hợp nữa. Bạn phải quay trở lại và kiểm tra lại khi bạn có khóa.

Thay đổi mã để sử dụng một vòng lặp, như:

synchronized(lock) { 
    while (nextVal == null) { 
     lock.wait(); 
    } 
    ... 

Bằng cách này, kiểm tra được thực hiện trong khi các chủ đề có khóa, và bất cứ điều gì xảy ra trong khối dưới vòng lặp while có thể chắc chắn NEXTVAL thực sự isn 't null.