Multithreaded Servers in Java

09 Nov, 2020
Prerequisites: Socket Programming in Java
Multithreaded Server: A server having more than one thread is known as Multithreaded Server. When a client sends the request, a thread is generated through which a user can communicate with the server. We need to generate multiple threads to accept multiple requests from multiple clients at the same time. 
Advantages of Multithreaded Server:
Quick and Efficient: Multithreaded server could respond efficiently and quickly to the increasing client queries quickly.
Waiting time for users decreases: In a single-threaded server, other users had to wait until the running process gets completed but in multithreaded servers, all users can get a response at a single time so no user has to wait for other processes to finish.
Threads are independent of each other: There is no relation between any two threads. When a client is connected a new thread is generated every time.
The issue in one thread does not affect other threads: If any error occurs in any of the threads then no other thread is disturbed, all other processes keep running normally. In a single-threaded server, every other client had to wait if any problem occurs in the thread.
Disadvantages of Multithreaded Server:
Complicated Code: It is difficult to write the code of the multithreaded server. These programs can not be created easily
Debugging is difficult: Analyzing the main reason and origin of the error is difficult.
Quick Overview
We create two java files, Client.java and Server.java. Client file contains only one class Client (for creating a client). Server file has two classes, Server(creates a server) and ClientHandler(handles clients using multithreading).
Client-Side Program: A client can communicate with a server using this code. This involves 
Establish a Socket ConnectionCommunication
Establish a Socket Connection
Communication
Java
import java.io.*;import java.net.*;import java.util.*;  // Client classclass Client {        // driver code    public static void main(String[] args)    {        // establish a connection by providing host and port        // number        try (Socket socket = new Socket("localhost", 1234)) {                        // writing to server            PrintWriter out = new PrintWriter(                socket.getOutputStream(), true);              // reading from server            BufferedReader in                = new BufferedReader(new InputStreamReader(                    socket.getInputStream()));              // object of scanner class            Scanner sc = new Scanner(System.in);            String line = null;              while (!"exit".equalsIgnoreCase(line)) {                                // reading from user                line = sc.nextLine();                  // sending the user input to server                out.println(line);                out.flush();                  // displaying server reply                System.out.println("Server replied "                                   + in.readLine());            }                        // closing the scanner object            sc.close();        }        catch (IOException e) {            e.printStackTrace();        }    }}
Server-Side Program: When a new client is connected, and he sends the message to the server.
1. Server class: The steps involved on the server side are similar to the article Socket Programming in Java with a slight change to create the thread object after obtaining the streams and port number.
Establishing the Connection: Server socket object is initialized and inside a while loop a socket object continuously accepts an incoming connection.
Obtaining the Streams: The inputstream object and outputstream object is extracted from the current requests’ socket object.
Creating a handler object: After obtaining the streams and port number, a new clientHandler object (the above class) is created with these parameters.
Invoking the start() method: The start() method is invoked on this newly created thread object.
2. ClientHandler class: As we will be using separate threads for each request, let’s understand the working and implementation of the ClientHandler class implementing Runnable. An object of this class acts as a Runnable target for a new thread.
First, this class implements Runnable interface so that it can be passed as a Runnable target while creating a new Thread.
Secondly, the constructor of this class takes a parameter, which can uniquely identify any incoming request, i.e. a Socket.
Inside the run() method of this class, it reads the client’s message and replies.
Java
import java.io.*;import java.net.*;  // Server classclass Server {    public static void main(String[] args)    {        ServerSocket server = null;          try {              // server is listening on port 1234            server = new ServerSocket(1234);            server.setReuseAddress(true);              // running infinite loop for getting            // client request            while (true) {                  // socket object to receive incoming client                // requests                Socket client = server.accept();                  // Displaying that new client is connected                // to server                System.out.println("New client connected"                                   + client.getInetAddress()                                         .getHostAddress());                  // create a new thread object                ClientHandler clientSock                    = new ClientHandler(client);                  // This thread will handle the client                // separately                new Thread(clientSock).start();            }        }        catch (IOException e) {            e.printStackTrace();        }        finally {            if (server != null) {                try {                    server.close();                }                catch (IOException e) {                    e.printStackTrace();                }            }        }    }      // ClientHandler class    private static class ClientHandler implements Runnable {        private final Socket clientSocket;          // Constructor        public ClientHandler(Socket socket)        {            this.clientSocket = socket;        }          public void run()        {            PrintWriter out = null;            BufferedReader in = null;            try {                                      // get the outputstream of client                out = new PrintWriter(                    clientSocket.getOutputStream(), true);                    // get the inputstream of client                in = new BufferedReader(                    new InputStreamReader(                        clientSocket.getInputStream()));                  String line;                while ((line = in.readLine()) != null) {                      // writing the received message from                    // client                    System.out.printf(                        " Sent from the client: %s\n",                        line);                    out.println(line);                }            }            catch (IOException e) {                e.printStackTrace();            }            finally {                try {                    if (out != null) {                        out.close();                    }                    if (in != null) {                        in.close();                        clientSocket.close();                    }                }                catch (IOException e) {                    e.printStackTrace();                }            }        }    }}
Steps:
Compile both Client and Server programs.
Run the server first and then the Client.
Output
Java-Multithreading
Java
Java
Writing code in comment?
Please use ide.geeksforgeeks.org,
generate link and share the link here.
Interfaces in Java
ArrayList in Java
Stream In Java
Collections in Java
Multidimensional Arrays in Java
Singleton Class in Java
Stack Class in Java
Set in Java
Introduction to Java
Constructors in Java