java - NIO线程CPU使用率

标签 java multithreading nio

我在使用时遇到 CPU 使用问题 java.nio.channel.Selector。 当服务器线程启动时,它最初消耗 200% 的 cpu 资源,然后急剧下降到 0.1%。但如果它是由客户端连接的。该数字迅速增加到 97% - 100%,并且即使在客户端断开连接后也保持该数字。

这是我为服务器编写的代码。

package com.cs.gang.test;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Iterator;

public final class TCPConnectionServer implements Runnable {

private RandomAccessFile logFile;
private Selector selector;

public TCPConnectionServer() throws IOException {
    final File logFile = new File("server_log.txt");
    if (logFile.exists()) {
        logFile.delete();
    }

    this.logFile = new RandomAccessFile(logFile.getCanonicalPath(), "rw");
    System.out.println(logFile.getCanonicalPath());

    selector = Selector.open();
    final ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
    serverSocketChannel.configureBlocking(false);
    serverSocketChannel.socket().bind(new InetSocketAddress(8888));
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

}

@Override
public void run() {
    while (true) {
        try {
            if (selector.select() > 0) {
                final Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
                while (keys.hasNext()) {
                    final SelectionKey key = keys.next();
                    keys.remove();

                    if (key.channel() instanceof SocketChannel) {
                        if (!((SocketChannel) key.channel()).isConnected()) {
                            logFile.writeChars(((SocketChannel) key.channel()).toString() + " is off line");
                        }
                    }

                    if (key.isAcceptable()) {
                        final ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                        final SocketChannel clientChannel = serverSocketChannel.accept();
                        clientChannel.configureBlocking(false);
                        clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
                        logFile.writeChars(clientChannel.toString() + "is now connected");
                    } else if (key.isReadable()) {

                        final SocketChannel client = (SocketChannel) key.channel();
                        if (client.isConnected()) {
                            final ByteBuffer buffer = ByteBuffer.allocate(1024);
                            int byteRead = -1;
                            final StringBuilder sb = new StringBuilder(client.toString()).append(" : ");

                            while ((byteRead = client.read(buffer)) > 0) {
                                sb.append(new String(buffer.array()), 0, byteRead);
                                buffer.clear();
                            }
                            logFile.writeChars(sb.toString());
                            System.out.println(sb.toString());
                        } else {
                            System.out.println("Closed Connection detected");
                        }

                    }
                }
            } else {
                System.out.println("Sleep for 100ms");
                Thread.sleep(100);
            }
        } catch (final IOException e) {
            e.printStackTrace();
        } catch (final InterruptedException e) {
            e.printStackTrace();
        }
    }
}

public static void main(String[] args) throws IOException {
    new Thread(new TCPConnectionServer()).start();
}
}

谁能帮帮我吗?我是蔚来新手,现在对这个问题完全不知道。

谢谢

最佳答案

clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);

问题就出在这里。 OP_WRITE 几乎总是准备就绪,因此您的选择器很少会阻塞并且通常会旋转。这是对 OP_WRITE 的误用。正确的使用方法如下:

  1. 有东西要写就写。
  2. 如果write()返回零,则为OP_WRITE注册套接字并返回到选择循环。当然,您还必须保存与 channel 相关联的写入的 ByteBuffer:这通常是通过 SelectionKey 的附件直接完成或间接地。理想情况下,每个 channel 都有一个读取和写入的ByteBuffer,保存在 channel 上下文对象中,该对象又保存为 key 附件。
  3. OP_WRITE 触发时,继续从该 ByteBuffer 写入。如果此操作完成,即 write() 不会返回零或短值写入计数,de-注册 OP_WRITE 的 channel 。

关于java - NIO线程CPU使用率,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28756547/

相关文章:

java - 检查 RemoteView 开关的 boolean 值的方法?

java - 是否可以添加另一个字段来查找和过滤 java 中 mongodb 数据库的结果?

java - 每 x 次调用函数而不阻塞 GUI Java

Python 线程随机返回错误结果

java - 为什么我的 Spring webflux 应用程序在每个请求上都会生成临时文件?

java - 如何将 InputStream 复制到 AsynchronousFileChannel

Java nio SelectionKey.register 和interestops

java - Spring MVC : adding securityConfig. xml 以及dispatcherContext.xml

java - JTextPane 中的文字换行

python - Python 中的 IRC 客户端;不是 IRC 机器人