Basic Distributed Counter using Java Sockets
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.
Introduction
A distributed counter is a type of data structure used to maintain a count across multiple machines or processes, which might not necessarily share any common physical memory. In many modern applications, from web traffic analytics to large-scale batch processing, the ability to count across many nodes is critical. Here, we focus on implementing a basic distributed counter using Java sockets, a foundational technique for network communication in Java that allows for sending and receiving data amongst distributed systems.
Sockets in Java
Java sockets are endpoints for communication between two machines. The java.net.Socket class implements client-side sockets and java.net.ServerSocket handles the server-side. Sockets rely on TCP/IP protocol to ensure reliable communication. When a server wants to receive data, it needs to open a server socket on a specified port. Clients connect by targeting the IP address and port, establishing a TCP connection for data exchange.
Implementing a Distributed Counter
The idea behind a basic distributed counter is to have a server that maintains the count and clients that send increment (or decrement) requests to the server. The server updates its count based on the received requests and might also need to handle concurrency control if multiple clients update the count simultaneously.
Step-by-step Implementation:
Step 1: Setting up the Server
First, we create a CounterServer that listens to client requests to either increment or decrement the counter:
This server listens on port 1234 and spawns a new thread (CounterHandler) for each connected client.
Step 2: Implementing the Client
A client connects to the server, sends commands and reads the updated count:
This client connects to the CounterServer, sends increment and decrement commands, and prints out the counter value as received from the server.
Handling concurrency
To ensure thread safety when updating the counter, the run() method in CounterHandler includes a synchronized block. This synchronization is crucial when dealing with shared mutable state (the count variable) across multiple threads.
Challenges and Considerations
- Scalability: As the number of clients increases, the server could become a bottleneck. More sophisticated strategies such as sharding the counter or using distributed cache or storage might be required for scalability.
- Reliability: The basic implementation lacks features like reconnecting in case the server crashes or goes down.
Conclusion
This basic implementation of a distributed counter using Java sockets introduces the concepts of networking, concurrency, and distributed counting. In production systems, additional complexity such as handling failovers, scaling to accommodate more clients, and using more robust counter synchronization or distribution methods would be necessary.
Related reading
- Basics to how a distributed, consistent key-value storage system return the latest key when dealing with concurrent requests?
- Behavior of channels in confirm mode with RabbitMQ
- Best architectural approaches for building iOS networking applications REST clients
- Best of breed indexing data structures for Extremely Large time-series
- Basic http file downloading and saving to disk in python?
- Bazel build behind proxy
- Bean instantiation via factory method failed; exception org.springframework.beans.BeanInstantiationException
- BeforeStep not being called in AsyncProcessor

System Design Fundamentals
Build a strong foundation in designing scalable, reliable distributed systems.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
System Design practice on Codemia
Work through 120+ system design problems with detailed solutions, from rate limiters to multi-region storage.