Best Practices for Multithreading in
Java
1. Synchronization:
If you have shared mutable data,
synchronize access to it. Synchronization makes sure that an object is seen
only in a consistent state by any thread, as it ensures mutual exclusion. It
also guarantees reliable communication between threads as it ensures that each
thread entering a synchronized region sees the effect of all previous
modifications that were guarded by the same lock.
2. Use Of Locks:
You should prefer using internal locks to
entire object locking, by not synchronizing on the ‘this’ object lock. You can
create some private lock objects (private Object lock = new Object();) and use
them for related operations. When you synchronize internally you can make use
of many techniques such as lock splitting, lock stripping, and non-blocking
concurrency control to achieve high concurrency.
3. Executer
Framework:
When writing new code or refactoring code,
do not use threads directly, but use the executer framework that consists of
executors and tasks, that splits the duties of a thread. A task is the actual
unit of work, and the executor service executes them. A task can be a Runnable
or a Callable. Callable is like Runnable except that it returns value. Executor
service can do many things such as wait for tasks to complete, wait for
graceful termination, retrieve the results of tasks etc.
4. Wait
Method:
The wait method, if still used in code,
must be invoked inside a synchronized region that locks on the object on which
it is invoked and also, we should use the wait-loop idiom always, where you
invoke wait inside a loop. The loop will test the condition before and after
waiting. Testing the condition before is required to ensure liveness because if
the condition already holds and notify or notifyAll has been already invoked,
there is no guarantee that thread will ever wake up from wait. Testing the
condition after is necessary because if the thread proceeds with the action
when the condition does not hold, the results can be unexpected. A thread might
wake up when the condition does not hold due to many reasons such as another
thread could have changed the condition between the notify invocation and
wakeup, another thread could have invoked notify accidentally, a notifyAll was
invoked even if condition is not satisfied for some threads, or a thread could
rarely wakeup in the absence of notify, known as a spurious wakeup.
5. Use Thread.sleep
instead of Thread.yield
Use Thread.sleep(1) instead of
Thread.yield() as Thread.yield can, according to java spec, simply return
control to the caller without doing anything; and even Thread.sleep(0) can
return immediately.
6. Size of Thread Pool
You
should design the size of the thread pool such that the average number of
runnable threads is not significantly greater than the number of processors.
You should also keep tasks reasonable small and independent of one another