Search code examples
javamultithreadingsocketsheavy-computation

Running thread takes much cpu


I am using below client thread for connecting to my NIO server.

    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();
        }
    }

}

This is my server side code. when I running the server side only the CPU is not more than 5% but when I run client for each thread cpu usage will raise about 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);
    }
}

Every thing works right. Client and server can see each other and communicate. In order to testing how much connection my code can accept I create several instances of above thread and here is the problem.

When I tracking the performance sector of my task panel(windows) by generating each instance of this thread the CPU usage of my PC (I am using a 2.6 core i5 CPU) raise with 30% and by generating 3 thread my cpu usage is about 100% !!!

I am wondering what is the problem with above code that takes 30% of my CPU.


Solution

  • I can see two potential causes for high CPU load.

    1. You are using non-blocking I/O inappropriately, by (in effect) repeatedly polling the channel to complete the connect, and to read data. In this particular use-case, you would be better advised to used blocking I/O. The data throughput will be (pretty much) the same, and you won't waste CPU by polling.

      Generally speaking, non-blocking I/O is only a good idea when the thread has other things to do instead of blocking.

    2. Writing to System.out could also use significant CPU ... external to the JVM. If standard output goes to a typical console application that displays it on the screen, then the process of rendering and painting the text onto the screen ... and scrolling ... could use a fair amount of CPU.