Запуск потока занимает много процессора

Я использую ниже клиентский поток для подключения к моему серверу 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();
        }
    }

}

Это мой код на стороне сервера. когда я работаю на стороне сервера, только процессор не превышает 5%, но когда я запускаю клиент для каждого потока, загрузка процессора возрастет примерно на 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), генерируя каждый экземпляр этого потока, загрузка ЦП моего ПК (я использую 2,6-ядерный ЦП i5) увеличивается на 30%, а при создании 3-х потоков мое использование процессора составляет около 100%!!!

Мне интересно, в чем проблема с приведенным выше кодом, который занимает 30% моего процессора.

2 ответа

Решение

Я вижу две возможные причины высокой загрузки процессора.

  1. Вы используете неблокируемый ввод / вывод не по назначению, (по сути) неоднократно опрашивая канал, чтобы завершить соединение и прочитать данные. В этом конкретном случае вам лучше использовать блокирующий ввод / вывод. Пропускная способность данных будет (в значительной степени) одинаковой, и вы не будете тратить ЦП на опрос.

    Вообще говоря, неблокирующий ввод-вывод - это хорошая идея, только когда у потока есть другие дела вместо блокирования.

  2. Запись в System.out может также использовать значительный процессор... внешний по отношению к JVM. Если стандартный вывод поступает в типичное консольное приложение, которое отображает его на экране, тогда процесс рендеринга и рисования текста на экране... и прокрутки... может потребовать достаточного количества ЦП.

Не вижу ничего особенного, что могло бы вызвать чрезмерный стресс, но я бы посоветовал вам включить в свой цикл короткий сон, чтобы не перегружать процессор. Это включает в себя отключение чашки от вашего основного потока (который может быть сервером, который должен выполнять важную работу по прослушиванию входящих соединений).

Особенно мне хотелось бы спать там, где выполняются внешние действия и ожидаются задержки.

Смотрите также Thread.sleep.

Сделав это, я проверил бы все по сравнению с вашим оригиналом, отслеживая процессор, память, загрузку и т. Д.

Другие вопросы по тегам