NIO 线程 CPU 用法
NIO Thread CPU usage
我在使用 运行 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();
}
}
谁能帮帮我?我是 NIO 的新手,我现在完全不知道这个问题。
谢谢
clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
问题就在这里。 OP_WRITE
几乎总是准备就绪,因此您的选择器很少会阻塞并且通常会旋转。这是对 OP_WRITE
的误用。正确的使用方法如下:
- 有事就写。
- 如果
write()
return 为零,则将 OP_WRITE
和 return 的套接字注册到选择循环。当然,您还必须保存与频道关联的 ByteBuffer
的写作来源:这通常是通过 SelectionKey
的附件直接或间接完成的。理想情况下,每个通道都有读取和写入 ByteBuffer
,保存在通道上下文对象中,该对象又保存为密钥附件。
- 当
OP_WRITE
触发时,从 ByteBuffer.
开始继续写入 如果完成,即 write()
没有 return 零或短写入计数,de-为OP_WRITE.
注册频道
我在使用 运行 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();
}
}
谁能帮帮我?我是 NIO 的新手,我现在完全不知道这个问题。
谢谢
clientChannel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
问题就在这里。 OP_WRITE
几乎总是准备就绪,因此您的选择器很少会阻塞并且通常会旋转。这是对 OP_WRITE
的误用。正确的使用方法如下:
- 有事就写。
- 如果
write()
return 为零,则将OP_WRITE
和 return 的套接字注册到选择循环。当然,您还必须保存与频道关联的ByteBuffer
的写作来源:这通常是通过SelectionKey
的附件直接或间接完成的。理想情况下,每个通道都有读取和写入ByteBuffer
,保存在通道上下文对象中,该对象又保存为密钥附件。 - 当
OP_WRITE
触发时,从ByteBuffer.
开始继续写入 如果完成,即write()
没有 return 零或短写入计数,de-为OP_WRITE.
注册频道