Writing thread-safe code is managing access to
state and in particular to shared, mutable state.
Object‘s state is its data, stored in state
variables such as instance or static fields.
Whether an object needs to be thread-safe depends
on whether it will be accessed from multiple threads.
Synchronization includes
Synchronized keyword
Volatile variables
Explicit locks
Atomic variables
Ways to avoid inappropriate synchronization with
multiple threads access.
Don‘t share the state variable across threads;
Make the state variable immutable; or
Use synchronization whenever accessing the
state variable.
Good practice on designing thread-safe classes
Good object-oriented techniques -
encapsulation(Don‘t expose the accessibility of the fields of the class too
much).
Immutability
Clear specification of
invariants
2.1 What is Thread Safety
A class is thread-safe if it behaves correctly
when accessed from multiple threads, regardless of the scheduling or
interleaving of the execution of those threads by the runtime environment, and
with no additional synchronization or other coordination on the part of the
calling code.
Thread safe classes encapsulate any needed
synchronization so that clients need not provide their own.
Stateless objects are always
thread-safe.
eg. It is only when servlets want to remember
things from one request to another that the thread safety requirement becomes an
issue.
2.2 Atomicity
Classic operation scenario
read-modify-write: To
increment a counter, you have to know its previous value and make sure no one
else changes or uses that value while you are in mid‐update.
check‐then‐act: you observe
something to be true and then take action based on that observation (create
X); but in fact the observation could have become invalid between the time you
observed it and the time you acted on it (someone else created X in the
meantime), causing a problem (unexpected exception, overwritten data, file
corruption).
2.2.1 Race Conditions
A race condition occurs when the correctness of a
computation depends on the relative timing or interleaving of multiple threads
by the runtime.
Operations A and B are atomic with respect to
each other, from the perspective of a thread executing A, when another thread
executes B, either all of B has executed or none of it has. An atomic operation
is one that is atomic with respect to all operations, including itself, that
operate on the same state.
The java.util.concurrent.atomic
package contains atomic variable classes for effecting atomic state transitions
on
numbers and object references. By
replacing the long counter with an AtomicLong, we
ensure that all actions that
access the counter state are
atomic.
Principle
Where practical, use existing thread-safe
objects, like AtomicLong, to manage your class‘s state. It is simpler to reason
about the possible states and state transitions for existing thread-safe objects
than it is for arbitrary state variables, and this makes it easier to maintain
and verify thread safety.
2.3 Locking
Dealing with more than one states currency
operations within one object.
To preserve state consistency, update related
state variables in a single atomic
operation.
2.3.1 Intrinsic Locks
A synchronized block has two parts: a reference
to an object that will serve as the lock, and a block of code to be guarded by
that lock. A synchronized method is shorthand for a synchronized block that
spans an entire method body, and whose lock is the object on which the method is
being invoked. (Static synchronized methods use the Class object for the
lock.)
2.3.2 Reentrancy
Reentrancy means that locks are acquired on a
per-thread rather than per-invocation basis.
When a thread acquires a previously unheld lock,
the JVM records the owner and sets the acquisition count to one. If that same
thread acquires the lock again, the count is incremented, and when the owning
thread exits the synchronized block, the count is decremented. When the count
reaches zero, the lock is released.
2.4 Guarding State with Locks
For each mutable state variable that may be
accessed by more than one thread, all accesses to that variable must be
performed with the same lock held.
For every invariant that involves more than one
variable, all the variables involved in that invariant must be guarded by the
same lock.
put-if-absent operation
2.5 Liveness and Performance

Refined solution for concurrent operation
on more than on state object inside the Class instance
There is frequently a tension between
simplicity and performance. When implementing a synchronization policy, resist
the temptation to prematurely sacrifice simplicity (potentially compromising
safety) for the sake of performance.
Avoid holding locks during lengthy
computations or operations at risk of not completing quickly such as network
or console I/O.