【问题标题】:Daemon Threads, thread count, and total started thread count守护程序线程、线程计数和总启动线程计数
【发布时间】:2016-09-06 09:12:39
【问题描述】:

我有一个用于 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(供客户端轮询消息)和服务器套接字(供客户端发送消息)。如果需要其他课程,请告诉我。

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

【问题讨论】:

  • 请收下您的邮政编码
  • 您可以在您正在使用的分析工具中查看线程信息。例如,jconsolejvisualvm 在“线程”选项卡中显示所有线程信息。进程中还会运行一些分析器线程,这将增加计数。
  • 我们不知道你的代码,所以我不知道如何回答。如果我是你,我会在 # of client 为 0 时进行线程转储,然后在为 1 时进行线程转储并比较它们,你会得到答案
  • @qwwdfsad 我刚刚用accept 代码更新了我的问题
  • 为什么要使用RMI,有什么特殊需要吗?使用 RMI,您仅限于同一子网(客户端需要与服务器一样位于某个子网中)。有这个要求吗?但这清楚地表明,由于 RMI,JVM 中有额外的线程。

标签: java multithreading profiling jvisualvm


【解决方案1】:

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

从 netty 页面查看example,其中在引导服务器时使用了 2 个 NioEventLoopGroup。

这 2 个线程是不阻塞传入流量所必需的。

【讨论】:

  • 您的代码会阻塞主线程,直到套接字接收到连接。 start(); 内部会发生什么,因为在接受连接后第一次调用此方法。
【解决方案2】:

我只是希望您查看了 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();
    }
}

【讨论】:

  • 如果有帮助,我为服务器添加了一段代码。
  • 我看到了.. 你真的需要 RMI 吗?您知道相同的子网限制吗?
  • 嗯,客户端实际上是状态机,只能通过轮询数据来工作。 RMI 是我们能做到的唯一方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多