In Part 1, I looked at how Java and Go approach concurrency differently at a high level.
Java tends to frame concurrency around threads, while Go emphasizes tasks and coordination.
After understanding that shift, a new question came up naturally:
What happens when concurrent tasks need to share data?
This is where concurrency becomes more challenging, and where Java and Go begin to differ more noticeably.
The Problem: Shared State
Consider a simple requirement:
Multiple concurrent tasks need to update a shared counter.
This kind of problem appears frequently:
- tracking active jobs
- counting processed items
- reporting progress
Although this example uses a counter, the same ideas apply to other shared state such as maps, slices, or in-memory caches that multiple concurrent tasks need to access safely.
How This Is Commonly Done in Java
In Java, sharing data safely usually means protecting it.
Using synchronized

Here, Java ensures that only one thread can access the counter at a time.
Using AtomicInteger

With atomics, we avoid explicit locks, but the idea is the same:
shared memory must be handled carefully.
How This Feels in Java
When writing code like this in Java, the typical concerns are:
- This data is shared
- Multiple threads can access it
- It must be protected from race conditions
Concurrency often feels like defending shared state from concurrent access.
The Same Problem in Go
Go provides similar tools — but it also encourages a different way of thinking.
Option 1: Protect Shared Data with a Mutex
The most direct translation from Java looks like this:

This approach works and is perfectly valid Go.
A mutex is a good fit when you truly have shared state — such as maps, caches, or simple counters — that multiple goroutines need to read and update safely. In these cases, protecting the data with a mutex is often the simplest and clearest solution.
Option 2: Avoid Sharing Data by Giving It an Owner
Go also makes it easy to approach the problem differently:
Don’t share the data at all.
Instead, give the data a single owner and communicate with it.
- only one goroutine owns count
- other goroutines send messages
- no locks are needed
Compared to Java, this approach offers a different and often simpler way to reason about concurrency.
A Small Note on Channels
Channels in Go can be buffered or unbuffered.
In this example, the channel is unbuffered, meaning sending and receiving must happen at the same time. This makes coordination very explicit.
Buffered channels allow limited queuing and can be useful when some decoupling between goroutines is needed. I’ll explore this more in the next part.
The Key Difference I Noticed
At this point, the mental difference became clear.
Java’s usual approach
- Share data between threads
- Protect it with locks or atomics
- Ensure thread safety at every access point
Go’s encouraged approach
- Reduce shared ownership
- Let one goroutine own the data
- Communicate through channels
Both approaches are valid.
But Go makes it easier to design away shared state instead of constantly protecting it.
A Simple Rule That Helped Me
While learning Go, this simple checklist helped me decide what to use:
- If multiple goroutines must update the same data → consider a single owner goroutine
- If shared access is unavoidable → use a mutex
- If it’s just a counter or flag → consider atomic operations
Atomic operations work well for simple counters or flags, but once multiple values need to stay consistent, a mutex or single-owner goroutine is usually the safer choice.
This mental model helps clarify Go’s approach to concurrency.
Takeaway from Part 2
- Java concurrency often focuses on protecting shared data
- Go encourages reducing shared ownership
- Mutexes are useful and sometimes the right choice
- Asking who owns this data? can simplify concurrent designs
This difference helps explain the intent behind Go’s concurrency model.
What’s Next
In Part 3, I’ll look at:
- worker pools and backpressure
- buffered vs unbuffered channels in practice
- graceful shutdown
- why goroutines are cheap (at a high level)

Top comments (0)