我正在使用以下客户端线程连接到我的NIO服务器。
class RunnableDemo implements Runnable {
private Thread t;
private String threadName;
InetAddress host = null;
int port = 9090;
RunnableDemo(String name) {
threadName = name;
System.err.println("Creating " + threadName);
}
public void run() {
System.err.println("Running " + threadName);
try {
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
socketChannel.connect(new InetSocketAddress(host, port));
while (!socketChannel.finishConnect())
;
System.out.println("Thread " + threadName + " Connected");
while (true) {
ByteBuffer buffer = ByteBuffer.allocate(1024);
if (socketChannel.read(buffer) != 0) {
buffer.flip();
byte[] bytes = new byte[buffer.limit()];
buffer.get(bytes);
System.out.println(threadName+ ":" + new String(bytes));
buffer.clear();
}
}
} catch (Exception e) {
System.out.println("Thread " + threadName + " interrupted.");
e.printStackTrace();
}
System.out.println("Thread " + threadName + " exiting.");
}
public void start() {
System.out.println("Starting " + threadName);
try {
host = InetAddress.getByName("127.0.0.1");
if (t == null) {
t = new Thread(this, threadName);
t.start();
}
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
}
这是我的服务器端代码。当我运行服务器端时,只有CPU不超过5%但是当我为每个线程运行客户端时,cpu的使用率会提高约20-30%
public class EchoServer {
private static final int BUFFER_SIZE = 1024;
private final static int DEFAULT_PORT = 9090;
private long numMessages = 0;
private long loopTime;
private InetAddress hostAddress = null;
private int port;
private Selector selector;
// The buffer into which we'll read data when it's available
private ByteBuffer readBuffer = ByteBuffer.allocate(BUFFER_SIZE);
int timestamp=0;
public EchoServer() throws IOException {
this(DEFAULT_PORT);
}
public EchoServer(int port) throws IOException {
this.port = port;
hostAddress = InetAddress.getByName("127.0.0.1");
selector = initSelector();
loop();
}
private Selector initSelector() throws IOException {
Selector socketSelector = SelectorProvider.provider().openSelector();
ServerSocketChannel serverChannel = ServerSocketChannel.open();
serverChannel.configureBlocking(false);
InetSocketAddress isa = new InetSocketAddress(hostAddress, port);
serverChannel.socket().bind(isa);
serverChannel.register(socketSelector, SelectionKey.OP_ACCEPT);
return socketSelector;
}
private void loop() {
for (;true;) {
try {
selector.select();
Iterator<SelectionKey> selectedKeys = selector.selectedKeys()
.iterator();
while (selectedKeys.hasNext()) {
SelectionKey key = selectedKeys.next();
selectedKeys.remove();
if (!key.isValid()) {
continue;
}
// Check what event is available and deal with it
if (key.isAcceptable()) {
accept(key);
} else if (key.isWritable()) {
write(key);
}
}
Thread.sleep(3000);
timestamp+=3;
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}
private void accept(SelectionKey key) throws IOException {
ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.setOption(StandardSocketOptions.SO_KEEPALIVE, true);
socketChannel.setOption(StandardSocketOptions.TCP_NODELAY, true);
socketChannel.register(selector, SelectionKey.OP_WRITE);
System.out.println("Client is connected");
}
private void write(SelectionKey key) throws IOException {
SocketChannel socketChannel = (SocketChannel) key.channel();
ByteBuffer dummyResponse = ByteBuffer.wrap(("ok:" + String.valueOf(timestamp)) .getBytes("UTF-8"));
socketChannel.write(dummyResponse);
if (dummyResponse.remaining() > 0) {
System.err.print("Filled UP");
}
System.out.println("Message Sent");
// key.interestOps(SelectionKey.OP_READ);
}
}
每件事都合适。客户端和服务器可以看到对方并进行通信。为了测试我的代码可以接受多少连接,我创建了上面线程的几个实例,这就是问题所在。
当我通过生成此线程的每个实例来跟踪我的任务面板(windows)的性能扇区时,我的PC的CPU使用率(我使用的是2.6核i5 CPU)提升30%并通过生成3个线程我的cpu使用率约为100%!!!
我想知道上面的代码占用了30%的CPU是什么问题。
答案 0 :(得分:2)
我可以看到两个可能导致高CPU负载的原因。
您正在不恰当地使用非阻塞I / O,通过(实际上)重复轮询通道以完成连接和读取数据。在这个特定的用例中,最好建议您使用阻塞I / O.数据吞吐量(几乎)相同,并且您不会通过轮询浪费CPU。
一般来说,当线程有其他事情要做而不是阻塞时,非阻塞I / O只是一个好主意。
写入System.out
也可能使用JVM外部的重要CPU。如果标准输出转到在屏幕上显示它的典型控制台应用程序,那么渲染和将文本绘制到屏幕上的过程......以及滚动...可能会使用相当数量的CPU。
答案 1 :(得分:-2)
看不到会引起过度压力的任何特殊情况,但我建议你在循环中加入一个短暂的睡眠,以确保你不会占用cpu。这包括从你的主线程中占用杯子(这可能是一个服务器,希望能够完成收听即将到来的连接的重要工作)。
我会特别沉睡在外部活动执行和预期延迟的地方。
另见Thread.sleep。
这样做之后,我会在监视cpu,内存,负载等的同时测试你的原始内容。