守护线程、线程计数和总启动线程计数

9

我有一个简单的Java多线程回显服务器代码(它将接收到的内容返回给客户端)。 我正在对服务器的各种资源进行性能分析,包括线程统计信息。 下面是一些这些统计数据,根据连接的客户端数量。我的问题是与基线(客户端数为0)相比非基线是什么!

1)为什么当单个客户端连接时,总线程数会增加2?(对于其他情况,递增1是有意义的)

2)这两个非守护进程线程是什么?为什么守护线程最初递增1,然后保持不变?

它们是某种随机类型吗?!

# clients                    0  1   2   3   4   5   6   7   8   9   10

Total Started Thread Count  15  18  19  20  21  22  23  24  25  26  27
Thread count                14  16  17  18  19  20  21  22  23  24  25
Peak thread count           14  16  17  18  19  20  21  22  23  24  25
Daemon thread count         12  13  13  13  13  13  13  13  13  13  13

这是服务器代码的片段。我同时使用了RMI(用于客户端轮询消息)和Server Socket(用于客户端发送消息)。如果需要其他类,请告诉我。

package test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.rmi.server.UnicastRemoteObject;
import java.util.Vector;

public class ServerRMI extends Thread implements Hello {
    //centralized token manager runs polling server and socket server to receive updated tokens
    static Vector<String> tokenList= new Vector<String>();
    protected Socket clientSocket;
    static int RMIRegistryPort=9001;
    static int SocketServerPort=9010;

    public static void main(String[] args) throws IOException {
        try {
            ServerRMI obj = new ServerRMI();
            Hello stub = (Hello) UnicastRemoteObject.exportObject(obj, 0);

            // Bind the remote object's stub in the registry
            Registry registry = LocateRegistry.createRegistry(RMIRegistryPort);
            registry.bind("Hello", stub);
            System.err.println("Server ready");
        } catch (Exception e) {
            System.err.println("Server exception: " + e.toString());
            e.printStackTrace();
        }

        ServerSocket serverSocket = null;
        //initialize token list
        //A needs to execute first
        tokenList.add(0,"0");

        try {
            serverSocket = new ServerSocket(SocketServerPort);
            System.out.println("Connection Socket Created");
            try {
                while (true) {
                    System.out.println("Waiting for Connection");
                    new ServerRMI(serverSocket.accept());
                }
            } catch (IOException e) {
                System.err.println("Accept failed.");
            }
        } catch (IOException e) {
            System.err.println("Could not listen on port: "+SocketServerPort);
        } finally {
            try {
                serverSocket.close();
            } catch (IOException e) {
                System.err.println("Could not close port: "+SocketServerPort);
            }
        }
    }

    private ServerRMI(Socket clientSoc) {
        clientSocket = clientSoc;
        start();
    }

    public ServerRMI() {}{
        // TODO Auto-generated constructor stub
    }

    public void run() {
        System.out.println("New Communication Thread Started");

        try {
            PrintWriter out = new PrintWriter(clientSocket.getOutputStream(),
                    true);
            BufferedReader in = new BufferedReader(new InputStreamReader(
                    clientSocket.getInputStream()));

            String inputLine;

            while ((inputLine = in.readLine()) != null) {
                tokenList.add(0,inputLine);
                System.out.println("Server received: " + inputLine);
//                  System.out.println(" ququ size: "+queue.size());
                out.println(inputLine);

                if (inputLine.equals("Bye."))
                    break;
            }

            out.close();
            in.close();
            clientSocket.close();
        } catch (IOException e) {
            System.err.println("Problem with Communication Server");
        }
    }

    public String pollServer() {
        if(!tokenList.isEmpty()){
            String data = tokenList.get(0);
            System.out.println("Poll data: "+data);
            return data;
        } else{
            return tokenList.size()+"";
        }
    }
}

请提供您的邮编。 - qwwdfsad
您可以在使用的性能分析工具中检查线程信息。例如,jconsolejvisualvm会在“线程”选项卡中显示所有线程信息。 在进程中还会运行一些分析器线程,这将增加计数。 - Pradhan
1
我们不知道你的代码,所以我不知道我们如何回答。如果我是你,我会在客户端数量为0时进行线程转储,然后当它为1时进行比较,你就会得到答案。 - Nicolas Filotto
客户端实际上是状态机,只能通过轮询数据来工作。RMI 是我们唯一的选择。 - Tina J
RMI 的计算成本更高吗? - Tina J
显示剩余2条评论
2个回答

3

我不确定你使用什么来接收这些连接,但通常处理TCP非阻塞连接的框架(如Netty)会使用一个主线程来监听端口和一个线程池来处理传入的连接。这意味着,如果线程池限制为1个线程,则至少会打开2个线程以处理传入的连接。

请参见Netty页面上的示例,在启动服务器时使用了2个NioEventLoopGroups。

这2个线程是必要的,以避免阻止传入的流量。


你的代码会阻塞主线程,直到socket接收到连接。在start();方法被调用后第一次接受连接时发生了什么? - Richard Burkhardt

3
我希望你能检查VisualVM分析器中的线程名称。通常,线程名称可以让你对启动了什么有一些想法。
ad 1) 这可能是一些TCP/IP后台(清理器、轮询器)线程,只要外部有一些TCP/IP连接就会生成。
当客户端回到0时会发生什么?额外的线程是否消失?
答案可能因JVM供应商和版本而异。所以我认为你需要查看JVM内部关于套接字和线程的内容。
以下示例更直接(不涉及RMI)。
import java.net.*; // for Socket, ServerSocket, and InetAddress
import java.io.*; // for IOException and Input/0utputStream

public class TCPEchoServer {

    private static final int BUFSIZE = 32; // Size of receive buffer
    public static void main(String[] args) throws lOException {
        if (args.length != i) // Test for correct # of args
            throw new lllegalArgumentException("Parameter(s): <Port>");
        int servPort = Integer.parselnt(args[0]);
        // Create a server socket to accept client connection requests
        ServerSocket servSock = new ServerSocket(servPort);
        int recvMsgSize; // Size of received message
        byte[] byteBuffer = new byte[BUFSlZE]; // Receive buffer 

    }

    for (;;) { // Run forever, accepting and servicing connections
        Socket clntSock = servSock.accept(); // Get client connection
        System.out.println("Handling client at " +
            clntSock.getInetAddress().getHostAddress() + " on port " +
            clntSock, getPort());
        InputStream in = clntSock, getlnputStream();
        OutputStream out = clntSock.getOutputStream();
        // Receive until client closes connection, indicated by-i return
        while ((recvMsgSize = in .read(byteBuffer)) != -I)
            out.write(byteBuffer, O, recvMsgSize);
        clntSock, close();
    }
}

我为服务器添加了一段代码,如果有帮助的话。 - Tina J
我看到了... 你真的需要RMI吗?你知道同一子网限制吗? - Mitja Gustin
客户端实际上是状态机,只能通过轮询数据来工作。RMI 是我们唯一的选择。 - Tina J

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接