2009-12-24 27 views
6

Giả sử rằng tôi có một sợi tiêu thụ các vật phẩm do một sợi khác tạo ra. Phương thức run của nó là như sau, với inQueue là một BlockingQueueTrong điều kiện nào BlockingQueue.take ném ngoại lệ bị gián đoạn?

boolean shutdown = false; 
while (!shutdown) { 
    try { 
     WorkItem w = inQueue.take(); 
     w.consume(); 
    } catch (InterruptedException e) { 
     shutdown = true; 
    } 
} 

Bên cạnh đó, một sợi khác nhau sẽ báo hiệu rằng không có nhiều hạng mục công trình bằng cách làm gián đoạn sợi chạy này. Sẽ có() ném một ngoại lệ bị gián đoạn nếu nó không cần chặn để truy xuất mục công việc tiếp theo. tức là nếu nhà sản xuất báo hiệu rằng nó được thực hiện đang lấp đầy hàng đợi công việc, có thể vô tình để lại một số mục trong inQueue hoặc bỏ lỡ gián đoạn không?

+1

Bạn gần như đã làm đúng. Thay vì có * người tiêu dùng * đặt "tắt máy" thành đúng khi bị gián đoạn, thay vào đó hãy * nhà sản xuất * đặt nó thành true trước khi ngắt người tiêu dùng. Lưu ý điều này A) giữ cho mọi thứ đẹp bằng cách tránh giá trị sentinel ("thuốc độc"), B) xử lý chính xác các lần đánh thức giả, và C) tổng quát hơn ở chỗ bạn có thể cố ý ngăn chặn người tiêu dùng xem hàng đợi có trống không. – user359996

Trả lời

4

Cách tốt nhất để báo hiệu chấm dứt hàng đợi chặn là gửi giá trị 'chất độc' vào hàng đợi cho biết việc tắt máy đã xảy ra. Điều này đảm bảo rằng hành vi mong đợi của hàng đợi được tôn trọng. Gọi Thread.interupt() có lẽ không phải là một ý tưởng hay nếu bạn quan tâm đến việc xóa hàng đợi.

Để cung cấp một số mã:

boolean shutdown = false; 
while (!shutdown) { 
    try { 
     WorkItem w = inQueue.take(); 
     if (w == QUEUE_IS_DEAD) 
      shutdown = true; 
     else 
      w.consume(); 
    } catch (InterruptedException e) { 
     // possibly submit QUEUE_IS_DEAD to the queue 
    } 
} 
+0

Điều này dường như hoạt động tốt nhất. Từ những gì tôi có thể nói trong những trường hợp hiếm hoi, việc ngắt 'thực hiện' có thể được gửi trước khi take() đánh thức do có nhiều thứ hơn trong hàng đợi. Để bảo vệ chống lại điều này, tôi đã phải thoát khỏi hàng đợi với một vòng lặp thứ hai anyway. – Ryan

3

Theo javadoc, phương pháp take() sẽ ném InterruptedException nếu gián đoạn trong khi chờ đợi.

+0

Điều đó có nghĩa là gì? "" nếu bị gián đoạn trong khi đợi "" CHỈNH SỬA: Không bao giờ. Tôi hiểu câu trả lời của FkYkko bên dưới. – WarLord

-1

Gói java.concurrency.utils được thiết kế và triển khai bởi một số bộ óc tốt nhất trong lập trình đồng thời. Ngoài ra, việc gián đoạn các chủ đề như một phương tiện để chấm dứt chúng được xác nhận rõ ràng bằng cuốn sách "Java Concurrency in Practice" của chúng. Do đó, tôi sẽ vô cùng ngạc nhiên nếu bất kỳ vật phẩm nào bị bỏ lại trong hàng đợi do bị gián đoạn.

+3

Hãy thử nó trong một thử nghiệm và được "cực kỳ ngạc nhiên" ;-) – FkYkko

+0

"Người thông minh làm cho nó" cộng "ngắt() ngắt chủ đề" không đòi hỏi "hàng đợi ngăn chặn không kiểm tra tình trạng gián đoạn cho đến khi họ đang trống rỗng". Ví dụ: ít nhất một triển khai (xem http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/ArrayBlockingQueue.java#ArrayBlockingQueue. mất) của ArrayBlockingQueue gọi ReentrantLock.lockInterruptibly(), kiểm tra trạng thái ngắt của luồng (ném một InterruptedException nếu bị gián đoạn) trước khi cố gắng lấy phần tử tiếp theo. – user359996

2

Tôi tự hỏi về điều tương tự và đọc javadoc cho take() Tôi tin rằng nó sẽ ném một ngoại lệ bị gián đoạn chỉ sau khi đã lấy tất cả các mục trong hàng đợi, vì nếu hàng đợi có mục, nó sẽ không phải "chờ đợi ". Nhưng tôi đã thực hiện một thử nghiệm nhỏ:

package se.fkykko.slask; 
import java.util.concurrent.ArrayBlockingQueue; 
import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.atomic.AtomicLong; 

public class BlockingQueueTakeTest { 

public static void main(String[] args) throws Exception { 
    Runner t = new Runner(); 
    Thread t1 = new Thread(t); 
    for (int i = 0; i < 50; i++) { 
     t.queue.add(i); 
    } 
    System.out.println(("Number of items in queue: " + t.queue.size())); 
    t1.start(); 
    Thread.sleep(1000); 
    t1.interrupt(); 
    t1.join(); 
    System.out.println(("Number of items in queue: " + t.queue.size())); 
    System.out.println(("Joined t1. Finished")); 

} 

private static final class Runner implements Runnable { 
    BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(100); 
    AtomicLong m_count = new AtomicLong(0); 

    @Override 
    public void run() { 
     try { 
      while (true) { 
       queue.take(); 
       System.out.println("Took item " + m_count.incrementAndGet()); 
       final long start = System.currentTimeMillis(); 
       while ((System.currentTimeMillis() - start) < 100) { 
        Thread.yield(); //Spin wait 
       } 
      } 
     } 
     catch (InterruptedException ex) { 
      System.out.println("Interrupted. Count: " + m_count.get()); 
     } 
    } 
} 

} 

Á hậu sẽ mất 10-11 mục và sau đó kết thúc ví dụ: mất() sẽ ném InterruptedException ngay cả khi vẫn còn là mặt hàng trong hàng đợi.

Tóm tắt: Sử dụng cách tiếp cận Thuốc độc thay thế, sau đó bạn có toàn quyền kiểm soát số lượng còn lại trong hàng đợi.

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