Daemon Threads, thread count, and total started thread count
Asked Answered
F

2

9

I have a simple code for a multi-threaded echo server in Java (it returns whatever received back to the clients). I'm profiling various resources of the server including the thread statistics. Below are some of these statistics as per number of connected clients. My questions is for the baseline (# of clients 0) compared with non-baselines!

1) why when a single client connects, the total thread count increases by 2? (for the rest, it makes sense to increment by 1)

2) What are the two non-daemon threads?! And why the daemon initially increment by 1 and then is fixed?

Are are they kind of random?!

# 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

Here is the piece of code for the server. I'm using both RMI (for clients to poll messages) and Server Socket (for clients to send messages). If other classes are needed let me know.

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()+"";
        }
    }
}
Fredella answered 11/5, 2016 at 6:29 Comment(7)
Post code of your accept pleaseElbowroom
You can check the thread information in the profiling tool that you are using. For example, jconsole or jvisualvm shows all the thread information in the "Threads" tab. There will be some profiler threads also running in the process, which will add to the count.Severity
We don't know your code, so I don't know how we can answer. If I were you, I would do a thread dump when # of client is 0 then when it is 1 and compare them, you will get your answerCollings
@Elbowroom I've just updated my question with the accept codeFredella
Why are you using RMI, is there a special need for it? Using RMI you're limited to same subnet (client needs to be in some subnet as server is). Was this requirement? But this is clear indication that there are extra threads in JVM, because of RMI.Inoperable
Well, the clients are actually state machines which can only work by polling data. RMI is the only way we can do it.Fredella
Is RMI much more computationally expensive?Fredella
H
3

I'm not sure what you use to receive those connections, but usually frameworks that handle TCP connections non-blocking like Netty use a master thread to listen to the port and a thread pool to handle incomming connections. This means it will at least open 2 threads for the incomming connection if the thread-pool is limited to 1 thread.

See this example from the netty page, where 2 NioEventLoopGroups are used while bootstrapping the server.

The 2 threads are necessary to not block incoming traffic.

Hollenbeck answered 16/5, 2016 at 6:17 Comment(1)
Your code blocks the main thread until the socket receives a connection. What happens inside the start(); because this method is called for the first time after a connection is accepted.Hollenbeck
I
3

I just hope you reviewed the Thread names in VisualVM profiler. Usually the thread names give you some idea of what is started.

ad 1) this could very well be some TCP/IP background (cleaner, poller) thread that gets spawned as soon as you have some TCP/IP connection from outside.

What does happen when clients go back to 0. Does extra thread disappear?

Answers may very well differ based on JVM vendor and version. So I think you need to look in JVM internals about sockets and threads.

Example below is more straight forward (without 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();
    }
}
Inoperable answered 19/5, 2016 at 11:23 Comment(3)
I added a piece of code for the server if that helps.Fredella
I saw.. Do you really need RMI? Are you aware of same subnet restriction ?Inoperable
Well, the clients are actually state machines which can only work by polling data. RMI is the only way we can do it.Fredella

© 2022 - 2024 — McMap. All rights reserved.