每个事件的NIO线程池

时间:2014-04-18 08:49:49

标签: java threadpool nio

我正在使用基于java NIO的接受来自客户端的连接(已配置的非阻塞),并且只读取客户端发送的数据。一旦连接的客户端将长时间坚持服务器,所以我使用单线程用于" selector.select"并且"接受",但连接的客户端将每隔 15秒发送一次消息,客户端数量为5000,每条消息的大小 150字节

我没有为客户端的每次读取创建新线程,而是决定拥有100个线程的线程池,但服务器无法从所有客户端读取数据,只需挂起。每次能够从所有客户端读取数据时创建新线程。

这是我的ReadEvent线程

class ReadEvent implements Runnable {

    private static Logger logger = Logger.getLogger(ReadEvent.class.getName());
    private SelectionKey key;

    /**
     * Constructor to initialize the thread with the key having read event
     * pending.
     * 
     * @param key
     *            SelectionKey having read event.
     **/
    public ReadEvent(SelectionKey key) {
        this.key = key;
    }

    /**
     * Method to read the data from the key.
     **/
    @Override
    public void run() {
        SocketChannel socketChannel = (SocketChannel) key.channel();
        synchronized (socketChannel) {
            if (socketChannel.isOpen()) {
                try {
                    ByteBuffer readBuffer = ByteBuffer.allocate(150);
                    int numRead = 0;
                    try {
                        /* ".read" is nonblocking */
                        numRead = socketChannel.read(readBuffer);
                        /*
                         * Some other IO error on reading socketChannel.
                         */
                    }
                    catch (IOException e) {
                        logger.debug(
                            "[run] Connection abruptly terminated from client",
                            e);
                        key.channel().close();
                        return;
                    }
                    if (numRead == -1) {// socket closed cleanly
                        key.channel().close();
                        return;
                    }
                    String data = null;
                    data = new String(readBuffer.array(),
                        Charset.forName("ASCII"));
                    /* Send the read data to the DataDispatcher Actor */
                    Main.getDataDispatcher().tell(data, ActorRef.noSender());
                }
                catch (IOException e) {
                    logger.debug("[run] ", e);
                    return;
                }
            }
            else {// socketChannel is closed
                try {
                    key.channel().close();// Sanitary close operation
                    return;
                }
                catch (IOException e) {
                }
            }
        }
    }
}

我无法弄清线程池上的重载,任何有关ReadThread实现的建议都会对我有所帮助。

UPDATE 1:固定线程池上的java.lang.OutOfMemoryError

调用读取事件的片段:

每次阅读的帖子:

try {
    if (!key.isValid()) {
        continue;
    }
    if (key.isAcceptable()) {
        this.accept(key);
    }
    else if (key.isReadable()) {
        new Thread(new ReadEvent(key)).start();
    }
}
catch (CancelledKeyException e) {// key has been canceled
}

以上代码段适用于数千名客户。

使用线程池

ExecutorService executor = Executors.newFixedThreadPool(100);

try {
    if (!key.isValid()) {
        continue;
    }
    if (key.isAcceptable()) {
        this.accept(key);
    }
    else if (key.isReadable()) {
        executor.execute(new ReadEvent(key));
    }
}
catch (CancelledKeyException e) {// key has been canceled
}

以上代码段不能为所有客户端提供服务,并且逐渐发现堆大小正在增加,并且大多数(几乎100%)的CPU用于 GC 最后得到 java.lang.OutOfMemoryError:超出GC开销限制异常

1 个答案:

答案 0 :(得分:0)

6.7年后,但是嘿,我遇到了类似的问题。问题是直到实际读取套接字[socketChannel.read(...)],该操作才被视为有效。 通过将SelectionKey提交到线程池,请记住该线程将独立于选择线程执行,这意味着该操作仍按照选择线程准备就绪,并将继续将操作提交到您的线程池,直到已提交的线程之一实际读取[通道和操作被视为无效,并已从就绪集中删除。 其他人已经提到的解决方案是读取选择线程中的字节,然后将通道和读取的内容传递到线程池中以进行后处理。