2009-06-29 14 views
13

Tôi có một câu hỏi cơ bản. Tại sao và như thế nào phương thức đăng ký SelectableChannel có thể chặn cuộc gọi. Hãy để tôi cung cấp một kịch bản.Chặn chuỗi Java khi đăng ký kênh có bộ chọn trong khi chọn() được gọi. Phải làm gì?

Tôi đã tạo đối tượng Selector trong lớp Đăng ký như sau.

private static Selector selector = Selector.open(); 

Tôi cũng có phương thức trong cùng một lớp (Đăng ký) để đăng ký kênh bằng bộ chọn.

public static SelectionKey registerChannel(SelectableChannel channel, 
      int ops) throws IOException { 

     channel.configureBlocking(false); 
     return channel.register(selector, ops); 
    } 

Và có một lớp khác có tên Yêu cầu, có phương thức đọc dữ liệu từ kênh, quy trình và cuộc gọi theo phương pháp đăng ký kênh.

selectonKey = Register.register(socketChannel, SelectionKey.OP_READ); 

Tại thời điểm này, luồng bị chặn, không cho biết đầu mối của nó đang chờ đợi điều gì. Tôi đã xác minh rằng bộ chọn đang mở. Vui lòng cung cấp cho tôi một số trợ giúp để hiểu cách tôi có thể giải quyết vấn đề này. Có khóa nào mà tôi có thể thả ra không.

Bất kỳ đầu vào nào cũng sẽ được đánh giá cao.

Thêm vào những gì tôi đã mô tả. Các thử nghiệm khác cho thấy rằng nếu phương thức Register.register được gọi từ cùng một luồng, nó có thể đăng ký nhưng sau đó nếu một số luồng khác cố gắng gọi phương thức, thì luồng không di chuyển về phía trước.

Trả lời

0

Bạn đã thử in theo dõi ngăn xếp của tất cả các chuỗi trong chương trình của mình (sử dụng kill -QUIT trong Unix hoặc Ctrl + Break trong Windows hoặc sử dụng tiện ích jstack)?

AbstractSelectableChannel chứa khóa mà configureBlockingregister cần đồng bộ hóa. Khóa này cũng có thể truy cập thông qua phương thức blockingLock() và do đó, một luồng khác có khả năng giữ khóa làm cho cuộc gọi đăng ký của bạn chặn không giới hạn (nhưng không có dấu vết ngăn xếp khó biết).

7

Bạn cần sử dụng khóa và đồng bộ hóa thủ công.

Trong cùng một sợi bạn đang chạy vòng lặp selector có một ReentrantLock:

final ReentrantLock selectorLock = new ReentrantLock(); 

Sau đó, khi bạn cần phải đăng ký với bộ chọn làm một cái gì đó như thế này:

selectorLock.lock(); 
try { 
    selector.wakeup(); 
    socketChannel.register(selector, ops); 
} finally { 
    selectorLock.unlock(); 
} 

Cuối cùng, trong bạn vòng lặp mà bạn đang gọi là accept(), giống như sau:

selectorLock.lock(); 
selectorLock.unlock(); 

selector.select(500); 

Và t hen tiếp tục với phần còn lại của logic của bạn.

Cấu trúc này đảm bảo rằng cuộc gọi register() sẽ không chặn bằng cách đảm bảo rằng không bao giờ có một số khác select() giữa các cuộc gọi wakeup()register() tương ứng.

+0

+1 cho mẫu mã. Tôi thứ hai, từ kinh nghiệm. Thực hiện tốt. – casey

+0

Điều này cho biết thêm tối đa 500 ms độ trễ không cần thiết cho mỗi đăng ký. Thay vào đó, hãy sử dụng phương pháp này trong http://stackoverflow.com/a/2179612/448970. –

+0

@DavidB. Nó không thêm bất kỳ độ trễ nào cho mỗi đăng ký. Đây là chuỗi chọn để chặn tối đa 500ms, không phải chuỗi đăng ký và chuỗi chọn sẽ luôn muốn chặn trong 'select()'. Các tuyên bố trong câu trả lời bạn hầu như không chính xác. – EJP

28

Đây là tính năng cơ bản của hầu hết các triển khai NIO không rõ ràng từ tài liệu.

Bạn cần phải thực hiện tất cả các cuộc gọi đăng ký từ cùng một chuỗi đang thực hiện lựa chọn hoặc deadlocks của bạn sẽ xảy ra. Thông thường điều này được thực hiện bằng cách cung cấp một hàng đợi đăng ký/hủy đăng ký/thay đổi lãi suất được ghi vào và sau đó selector.wakeup() được gọi. Khi chọn thread đánh thức nó kiểm tra hàng đợi và thực hiện bất kỳ hoạt động được yêu cầu nào.

+1

Đây là một tính năng cơ bản của việc triển khai * tất cả * NIO được xác định hoàn toàn trong Javadoc. Có ba đồng bộ lồng nhau trong khi bạn đang ở trong 'select(),' và 'register()' thử một trong số chúng. Kết quả không phải là một bế tắc nhưng một khối *, * chỉ kéo dài cho đến khi lệnh gọi 'select()' đồng thời trả về. Giải pháp là gọi 'wakeup()' trước 'register().' Điều đó buộc 'select()' bỏ chặn và trả về số không, nó giải phóng ba khóa của nó, cho phép 'register()' xác nhận nó cần và tiếp tục. – EJP

+2

Giải pháp được cung cấp trong chú thích ở trên là sai - ít nhất, được giải thích. Tùy thuộc vào lịch trình của các luồng, chuỗi chọn có thể hoàn thành toàn bộ vòng lặp và nhập lại select() trước khi chuỗi đăng ký có thể gọi thanh ghi(). – dsd

0

đăng ký kênh của bạn từ bất kỳ chủ đề:

synchronized (selectorLock2) { 
    selector.wakeup(); 
    synchronized (selectorLock1) { 
     channel.register(selector, ops); 
    } 
} 

loop chọn của bạn sẽ trông như thế này:

while (true) { 
    synchronized (selectorLock1) { 
     selector.select(); 
    } 
    synchronized (selectorLock2) {} 

    .... 
} 
+0

Bạn chỉ cần một khóa duy nhất như hiển thị trong https://stackoverflow.com/a/1112809/194894 – Flow

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