1. [Mandatory] Thread-safe should be ensured when initializing singleton instance, as well as all methods in it.
Note: Resource driven class, utility class and singleton factory class are all included.
2. [Mandatory] A meaningful thread name is helpful to trace the error information, so assign a name when creating threads or thread pools.
Positive example:
public class TimerTaskThread extends Thread {
public TimerTaskThread() {
super.setName("TimerTaskThread");
…
}
}
3. [Mandatory] Threads should be provided by thread pools. Explicitly creating threads is not allowed.
Note: Using thread pool can reduce the time of creating and destroying thread and save system resource. If we do not use thread pools, lots of similar threads will be created which lead to “running out of memory” or over-switching problems.
4. [Mandatory] A thread pool should be created by ThreadPoolExecutor
rather than Executors
. These would make the parameters of the thread pool understandable. It would also reduce the risk of running out of system resource.
Note: Below are the problems created by usage of
Executors
for thread pool creation:
1)FixedThreadPool
andSingleThreadPool
:
Maximum request queue sizeInteger.MAX_VALUE
. A large number of requests might cause OOM.
2)CachedThreadPool
andScheduledThreadPool
:
The number of threads which are allowed to be created isInteger.MAX_VALUE
. Creating too many threads might lead to OOM.
5. [Mandatory] SimpleDateFormat
is unsafe, do not define it as a static variable. If have to, lock or DateUtils
class must be used.
Positive example: Pay attention to thread-safety when using
DateUtils
. It is recommended to use as below:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
Note: In JDK8,
Instant
can be used to replaceDate
,Calendar
is replaced byLocalDateTime
,Simpledateformatter
is replaced byDateTimeFormatter
.
6. [Mandatory] remove()
method must be implemented by ThreadLocal
variables, especially when using thread pools in which threads are often reused. Otherwise, it may affect subsequent business logic and cause unexpected problems such as memory leak.
Positive example:
objectThreadLocal.set(someObject);
try {
...
} finally {
objectThreadLocal.remove();
}
7. [Mandatory] In highly concurrent scenarios, performance of Lock
should be considered in synchronous calls. A block lock is better than a method lock. An object lock is better than a class lock.
8. [Mandatory] When adding locks to multiple resources, tables in the database and objects at the same time, locking sequence should be kept consistent to avoid deadlock.
Note: If thread 1 does update after adding lock to table A, B, C accordingly, the lock sequence of thread 2 should also be A, B, C, otherwise deadlock might happen.
9. [Mandatory] A lock needs to be used to avoid update failure when modifying one record concurrently. Add lock either in application layer, in cache, or add optimistic lock in the database by using version as update stamp.
Note: If access confliction probability is less than 20%, recommend to use optimistic lock, otherwise use pessimistic lock. Retry number of optimistic lock should be no less than 3.
10. [Mandatory] Run multiple TimeTask
by using ScheduledExecutorService
rather than Timer
because Timer
will kill all running threads in case of failing to catch exceptions.
11. [Recommended] When using CountDownLatch
to convert asynchronous operations to synchronous ones, each thread must call countdown
method before quitting. Make sure to catch any exception during thread running, to let countdown
method be executed. If main thread cannot reach await
method, program will return until timeout.
Note: Be careful, exception thrown by sub-thread cannot be caught by main thread.
12. [Recommended] Avoid using Random
instance by multiple threads. Although it is safe to share this instance, competition on the same seed will damage performance.
Note:
Random
instance includes instances ofjava.util.Random
andMath.random()
.Positive example: After JDK7, API
ThreadLocalRandom
can be used directly. But before JDK7, instance needs to be created in each thread.
13. [Recommended] In concurrent scenarios, one easy solution to optimize the lazy initialization problem by using double-checked locking (referred to The Double-checked locking is broken Declaration), is to declare the object type as volatile
.
Counter example:
class Foo {
private Helper helper = null;
public Helper getHelper() {
if (helper == null) {
synchronized(this) {
if (helper == null)
helper = new Helper();
}
}
return helper;
}
// other functions and members...
}
14. [For Reference] volatile
is used to solve the problem of invisible memory in multiple threads. Write-Once-Read-Many can solve variable synchronization problem. But Write-Many cannot settle thread safe problem. For count++
, use below example:
AtomicInteger count = new AtomicInteger();
count.addAndGet(1);
Note: In JDK8,
LongAdder
is recommended which reduces retry times of optimistic lock and has better performance thanAtomicLong
.
15. [For Reference] Resizing HashMap
when its capacity is not enough might cause dead link and high CPU usage because of high-concurrency. Avoid this risk in development.
16. [For Reference] ThreadLocal
cannot solve update problems of shared object. It is recommended to use a static ThreadLocal
object which is shared by all operations in the same thread.