Concurrency boils down to managing shared state.
"All concurrency issues boil down to
coordinating access to mutable state.
The less mutable state, the easier it
is to ensure thread safety."
-- Java Concurrency in Practice
So the question you must ask yourself are:
- What is the inherent shared data that the my application will need?
- When can a thread work on a snapshot of the data, that is, it momentary work on a clone of the shared data?
- Can I identify known pattern and use higher-level abstraction rather than low-level locks and thread coordination, e.g. queues, executor, etc. ?
- Think of a global locking scheme as to avoid deadlock and have a consistent acquisition of locks
The simplest approach to manage shared state is to serialize every action. This coarse-grained approach results however into a high lock contention and poor performance. Managing concurrency can be seen an optimization exercise where you try to reduce the contention. So subsequent questions are:
- How would the simplest approach be?
- What are the simple choice that I can make to reduce contention (possibly with fine grained locking) and improve performance without making the solution overly complicated?
- When am I going too fined-grained, that is, the complexity introduced isn't worth the performance gain?
A lot of approach to reduce contention rely on some form of trade-off between what would be necessary to enforce the correct behavior and what is feasible to reduce contention.
- Where can I relax a few constraint and accept that sometimes stuff won't be 100% correct (e.g. a counter) ?
- Can I be optimistic and deal with conflict only when concurrent modifications happen (e.g. using time stamp and retry logic - that's what TM do)?
Note that I never worked on a game, only on server-side part of enterprise apps. I can imagine that it can be quite different.