Multi-Threading in Java – Client Server Application Example
Multithreading in java?
Multithreading in java is a process of executing multiple threads simultaneously. A thread is a lightweight sub-process, the smallest unit of processing. Multiprocessing and multithreading, both are used to achieve multitasking. … Java Multithreading is mostly used in games, animation, etc
Every modern operating system has the support for multi-threading–so does the application built to run on that operating system. No, doubt multi-threading programming increase the performance and concurrency in our application. For certain applications, it is easier to code different procedures calls and conversation in a different thread than to manage them from the same thread.
Mostly in a client-server application, you definitely want the create the server application in a multi-threaded environment where thousands of clients connected to one server. Well if you think that we’re going to build a client-server application than you’re right. Wait! don’t just close the tab because this time I have got a real-world problem.
Application Overview
First, a little intro to my application so that we’re all on the same page. The application is about to build a smart meter (GPRS Meter) data collector. The application simply opens up a ServerSocket and after that, it accepts clients (Smart Meter) waiting for the connection. There are thousands of smart meters trying to connect to that application. So, when the smart meter connects with the application via socket, the application creates its own separate thread for communication.
Get Started
The application that we’re going to build will be in Java programming language. So, the first step was simple, I try to open up the ServerSocket holding the Pepsi can in my hand.
public class DeviceListenerRunnable implements Runnable { private final int portNumber; // 1 private final ServerSocket serverSocket; private final IConnectionEventArgs connectionEventArgs; // 2 public DeviceListenerRunnable(int portNumber, IConnectionEventArgs connectionEventArgs) { this.portNumber = portNumber; serverSocket = new ServerSocket(portNumber); // 3 this.connectionEventArgs = connectionEventArgs; } @Override public void run() { Socket socket; while (!Thread.currentThread().isInterrupted()) { try { socket = serverSocket.accept(); // 4 connectionEventArgs.onDeviceConnected(socket, socket.getRemoteSocketAddress().toString()); // 5 if (!socket.isClosed()) new ReceiverThread(socket, connectionEventArgs).start(); // 6 } catch (Exception ignored) { } } } }
Here’s what’s going on inside the code:
- Port number for ServerSocket.
- An interface which notifies the caller that the new smart meter has connected. We’ll see the implementation of
IConnectionEventArgs
in a couple of minutes. - Creates the ServerSocket with the specified port number.
- Waiting for a new client (Smart Meter) to become online.
- Sending the Socket and info of connected smart meter back to the caller class.
- Creating a new separate thread for the connected smart meter.
Examining the ReceiverThread class
The ReceiverThread
class basically processing all the incoming data from the connected smart meter and send data back to caller class via IConnectionEventArgs
interface. Every smart meter connected to an application has its own instance of this class. So, if there are a thousand devices connected to the server than it means every device has it’s own instance of ReceiverThread
class because every device communicates in a separate thread. Let’s see the implementation of this class.
public class ReceiverThread extends Thread { private final Closeable closeable; private final IConnectionEventArgs connectionEventArgs; public ReceiverThread(@Nonnull Closeable closeable, IConnectionEventArgs connectionEventArgs) { this.closeable = closeable; this.connectionEventArgs = connectionEventArgs; } @Override public void run() { if (closeable instanceof Socket) { Socket socket = (Socket) closeable; while (!isInterrupted() && !socket.isClosed()) { // 1 String info = socket.getRemoteSocketAddress().toString(); try { byte[] data = handleTcp(socket); // 2 connectionEventArgs.onDataReceived(info, socket, data) // 3 } catch (IOException e) { connectionEventArgs.onDeviceDisconnected(info, e); // 4 try { interrupt(); // 5 socket.close(); // 6 } catch (IOException ignored) { } break; } } } } private byte[] handleTcp(Socket socket) throws IOException { // Logic for reading the input stream } }
Let’s take a brief look at different steps:
- Nothing out of ordinary happens simply start a while loop until the current thread is not interrupted or socket is closed.
- Handling the incoming TCP data from a smart meter. (If someone want to see the logic behind handleTcp data method just ask in the comment section)
- Sending the incoming data back to caller class. (We’ll see the caller class in a couple of minutes)
- If IOException occurs, notify the caller class that the smart meter has been disconnected.
- Needs to call the interrupt method on the current thread.
- Close socket for the smart meter.
Before going any further I think you guys need to see the legendary IConnectionEventArgs
interface implementation.
interface IConnectionEventArgs { void onDeviceConnected(Socket socket, String info); void onDeviceDisconnected(String info, IOException e); void onDataReceived(String info, Closeable c, byte[] data); }
Implementation of caller class
We got the basics down now we just need to implement the IConnectionEventArgs
interface and start the thread for ServerSocket class. So, the DeviceListenerRunnable
class listens to new incoming connections from smart meters.
public class ParentMediaHelper implements IConnectionEventArgs { private final Thread listenerThread; private final DeviceCollection deviceCollection = DeviceCollection.instance(); public ParentMediaHelper(int portNumber) { DeviceListenerRunnable meterListenerRunnable = new DeviceListenerRunnable(portNumber, this); listenerThread = new Thread(meterListenerRunnable); } public void startListener() { listenerThread.start(); } @Override public void onDeviceConnected(Socket socket, String info) { // handle new smart meter connection } @Override public void onDeviceDisconnected(String info, IOException e) { deviceCollection.remove(info) } @Override public void onDataReceived(String info, Closeable c, byte[] data) { if(containsSerialNumber(data)){ String serialNumber = extractSerialNumber(data) deviceCollection.insert(Device(info, c, serialNumber)) } // handle other things } }
I know, many of you think that there are thousands of smart meter connected to the server. How can I determine that the data came from which smart meter? Actually when the first time smart meter sends the data (current or voltage) after a successful socket connection. It also sends its serial number (unique identifier) and with that serial number, I create a new Device model and insert the model in my DeviceCollection class. The DeviceCollection
simply a singleton class performs the CRUD functionality.
Similarly when the onDeviceDisconnected calls we remove the device from our collection.
Running the Application Server
Nice! all the hard work is done. Now I just need the main function which simply calls the startListener method inside the ParentMediaHelper
class.
public class ddssd { private static final int PORT_NUMBER = 14001; // Always use port above 1024 public static void main(String[] args) { ParentMediaHelper mediaHelper = new ParentMediaHelper(PORT_NUMBER); mediaHelper.startListener(); } }
After running the above program, my application starts accepting connection from the smart meter.
Note: If you’re running application on Linux based OS you need to open the specified port.
Alright, guys, this was my story of creating a Java-based server application. If you like this article please share it with the community.
What’s Next
Implementation of above example with OpenJDK Project Loom, a light-weight concurrency framework.
Thank you for being here and keep reading…
@Miu Yes you’re right there’s no comparison of .net core with java API services. But this article is only on show how to connect multiple clients in Java. The real application that I built is on Kotlin with coroutines. Also in Kotlin application instead of creating new thread for every device I create a coroutine.
I work for a company with over 3M customers and at least that many smart meters. We chose to do it UDP, rxjava with a store and forward model built on Kafka. With 10 servers, handled the whole load of 1.5M/s.
Then we moved to .net core and now we’re handling the entire load (3M/s) simultaneously with 3 servers and a par identical stack.