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:

    1. public class TimerTaskThread extends Thread {
    2. public TimerTaskThread() {
    3. super.setName("TimerTaskThread");
    4. }
    5. }

    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 and SingleThreadPool:
      Maximum request queue size Integer.MAX_VALUE. A large number of requests might cause OOM.
      2) CachedThreadPool and ScheduledThreadPool:
      The number of threads which are allowed to be created is Integer.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:

    1. private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() {
    2. @Override
    3. protected DateFormat initialValue() {
    4. return new SimpleDateFormat("yyyy-MM-dd");
    5. }
    6. };

    Note: In JDK8, Instant can be used to replace Date, Calendar is replaced by LocalDateTime, Simpledateformatter is replaced by DateTimeFormatter.

    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:

    1. objectThreadLocal.set(someObject);
    2. try {
    3. ...
    4. } finally {
    5. objectThreadLocal.remove();
    6. }

    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 of java.util.Random and Math.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:

    1. class Foo {
    2. private Helper helper = null;
    3. public Helper getHelper() {
    4. if (helper == null) {
    5. synchronized(this) {
    6. if (helper == null)
    7. helper = new Helper();
    8. }
    9. }
    10. return helper;
    11. }
    12. // other functions and members...
    13. }

    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: 

    1. AtomicInteger count = new AtomicInteger();
    2. count.addAndGet(1);

    Note: In JDK8, LongAdder is recommended which reduces retry times of optimistic lock and has better performance than AtomicLong.

    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.