Multithreading Chat Server

Published on
17 December 2025
2 min read

Java to Go: From Locks to Lock-Free

A legacy Java TCP chat server had a fundamental flaw: shared mutable state protected by locks. Rebuilt from scratch in Go using single-owner goroutines and message passing instead.

GoConcurrencyCSPDockerPrometheus

Problem → Solution

ProblemJavaGo
SynchronizationMutex-heavy, deadlock riskNo locks anywhere
I/O ModelThread-per-connection, blockingGoroutines, non-blocking channels
Shared StateConcurrent mutations, race conditionsSingle Registry goroutine (sequential)
BackpressureThreads block on queuesDrop slow clients, unblock writer
Race SafetyManual review, hard to verifygo test -race (zero detections)

Architecture: Registry Goroutine as Single Source of Truth

Client 1 (read) ──┐
Client 1 (write)──┤
                  ├──> [Registry] <──────> [Shared State]
Client N (read) ──┤                        (users, messages)
Client N (write)──┘

All mutations flow through Registry via channels (CSP).
No concurrent access. No locks. No races.

Features: Register username, broadcast, whisper (/w), list users (/users).

Evidence: Zero Data Races

  • go test -race passes with zero detections across all state transitions
  • Test coverage: duplicate rejection, user list consistency, whisper routing
  • Prometheus metrics: connected clients, message throughput, latency (p50/p95/p99)
  • Grafana dashboard auto-loads with docker-compose up --build

View on GitHub →