从套接字流中读取的线程需要更多 CPU 使用

Thread for reading from socket stream takes more CPU usage

在client socket中,我写了一个线程来不断读取socket的inputStream。这里我使用了一个while循环来无限读取。但是需要更多CPU;因此是否有可能减少 CPU。请添加您的建议。 也可以为 inputStream 添加侦听器。

线程代码:

public void run() {
while (!shutdown) {
    try {
        if(socketClient != null) {
            String message = socketClient.getMessage();
            logger.info ("Message size:" + message.length ());
            if(!message.equals("EmptyString")) {
                process(message);
            }
        } 
    } catch (Exception exception) {
        logger.info("Unable to read the socket message" +exception);
    }
}

}

SocketClient.java

public class SocketClient{
private volatile boolean isConnected;
private int              port;
private int              retryCount;
private long             startTime;
private String           hostName;
private DataInputStream  input;
private DataOutputStream output;
private Socket           socket;

public SocketClient(int port, String hostname) throws IOException {
    this.port     = port;
    this.hostName = hostname;
    establishConnection();
}


public void shutdown() {
    try {
        shutdown = true;
        input.close();
        output.close();
        socket.close();
    } catch (Exception e) {
        logger.debug("Exception in shutdown:" + e.getMessage());
    }
}

        public String getMessage() {
    BufferedReader reader = null;

    try {
        StringBuilder builder = new StringBuilder();
        reader = new BufferedReader(new 
                        InputStreamReader(tcpSocket.getInputStream()));

        do {
            builder.append(reader.readLine());
        } while((reader.ready()));

        if (builder.length() == 0)
            return "EmptyString";

        return builder.toString();
    } catch (IOException e) {
            return "EmptyString";
    } finally {
        try {
            if(reader != null)
                reader.close();
        } catch(IOException e) {
            logger.error("unable to close reader");
        }
    }
}

    private void establishConnection() {
        retryCount = 1;
        startTime  = System.currentTimeMillis();

        while (!shutdown) {
            try {
                if(!isConnected) {
                    socket = new Socket(hostName,port);
                    socket.setKeepAlive(true);
                    input       = new DataInputStream(socket.getInputStream());
                    output      = new DataOutputStream(socket.getOutputStream());
                    isConnected = true;
                    shutdown    = true;
                } 
            } catch (Exception exception) {
                isConnected = false;
                sleepFewSeconds();
                reconnectSocket();
            }
        }
    }

    private void reconnectSocket() {
        long endTime = startTime + 120000L;

        if(!(System.currentTimeMillis() < endTime)) {
            shutdown = true;
        }   
    }

    private void sleepFewSeconds() {
        try {
            TimeUnit.MILLISECONDS.sleep(20);
        } catch (InterruptedException interruptedException) {
            shutdown = true;
        }
    }

}

我要在这里批评整个class。您的特定问题的答案将会出现。

public class SocketClient{
private volatile boolean isConnected;

你不需要这个。 socket == null 也行。

private int              port;
private int              retryCount;
private long             startTime;
private String           hostName;
private DataInputStream  input;
private DataOutputStream output;
private Socket           socket;

public SocketClient(int port, String hostname) throws IOException {
    this.port     = port;
    this.hostName = hostname;
    establishConnection();
}


public void shutdown() {
    try {
        shutdown = true;
        input.close();
        output.close();
        socket.close();

您不需要所有这些关闭,而且您按错误的顺序执行它们。 output.close() 就足够了,无论如何它肯定是第一个。

    } catch (Exception e) {
        logger.debug("Exception in shutdown:" + e.getMessage());
    }
}

public String getMessage() {
    BufferedReader reader = null;

BufferedReader应该是实例变量,不是局部变量。它被缓冲了。如果将其设为局部变量,您将丢失数据。

    try {
        StringBuilder builder = new StringBuilder();
        reader = new BufferedReader(new 
                        InputStreamReader(tcpSocket.getInputStream()));

        do {
            builder.append(reader.readLine());
        } while((reader.ready()));

你不需要这一切。如果消息是单行,你只需要 return reader.readLine(),你需要 caller 检查它是否为空,如果是,则关闭套接字,停止阅读, etc. 如果消息多于一行,这是对 ready() 的误用:它当然 而不是 消息结束的指示符。从您的问题下的评论看来,您甚至不应该拥有 方法 :只需将套接字输入流直接连接到您的 XML 解析器并让 阅读。

        if (builder.length() == 0)
            return "EmptyString";

不要这样做。 Return "" 或空。不要为您的应用程序编写必须解码的 new 魔法字符串。

        return builder.toString();
    } catch (IOException e) {
            return "EmptyString";

同上。

    } finally {
        try {
            if(reader != null)
                reader.close();

你应该关闭这里的reader。关闭它会关闭套接字,因此您永远不会收到其他消息。

        } catch(IOException e) {
            logger.error("unable to close reader");
        }
    }
}

private void establishConnection() {
        retryCount = 1;
        startTime  = System.currentTimeMillis();

        while (!shutdown) {
            try {
                if(!isConnected) {
                    socket = new Socket(hostName,port);
                    socket.setKeepAlive(true);
                    input       = new DataInputStream(socket.getInputStream());
                    output      = new DataOutputStream(socket.getOutputStream());
                    isConnected = true;
                    shutdown    = true;

这里为什么要设置shutdowntrue?什么都没有关闭。这是一个全新的插座。

                } 
            } catch (Exception exception) {
                isConnected = false;
                sleepFewSeconds();
                reconnectSocket();
            }

练习不好。 Socket.connect(),由 new Socket(...) 内部调用,已经重试,而且你应该区分连接失败异常,而不是对它们都采用相同的策略。例如,'connection timeout' 已经阻塞了一分钟左右:您不需要 另一个 睡眠; 'connection refused' 意味着没有任何监听,所以重试完全没有意义。

    private void reconnectSocket() {
        long endTime = startTime + 120000L;

        if(!(System.currentTimeMillis() < endTime)) {
            shutdown = true;
        }   
    }

    private void sleepFewSeconds() {
        try {
            TimeUnit.MILLISECONDS.sleep(20);

这不是 'few seconds'。 20毫秒,这在网络编程中至少有两个数量级是不够的,当然应该有任何睡眠。

        } catch (InterruptedException interruptedException) {
            shutdown = true;

shutdown 似乎永远不会为假。我怀疑你是否想过它的真正含义,我怀疑你是否真的需要它。

至于你的调用代码:

public void run() {
while (!shutdown) {
    try {
        if(socketClient != null) {

如果 socketClient 为空,此循环将无意义地旋转。确定这个方法应该构造套接字客户端?

            String message = socketClient.getMessage();
            logger.info ("Message size:" + message.length ());

这里你没有检查 null 并且没有正确响应,这将是关闭套接字并退出循环。相反,你会在这里得到一个 NPE。

            if(!message.equals("EmptyString")) {
                process(message);

见上文。不要给自己发特殊的短信。如果有一天对等方需要发送那个会怎样?

            }
        } 
    } catch (Exception exception) {
        logger.info("Unable to read the socket message" +exception);

无法接受。这个 catch 在循环内部,它基本上忽略了异常。结果是,此循环将再次无意义地旋转任何异常。您正在调用的方法应该声明为 throw IOException,这就是您应该在此处捕获的全部内容。目前你甚至会在 NullPointerException 上旋转。