【 Problem Description 】

  • Online commodity pictures are blank pictures.

[Scope of influence]

  • APP home page, store list page, product page details

[Accident level]

  • P0

[Processing]

  • 11:23 Feedback app commodity pictures are mostly empty

  • 11:30 Locating background interface Cache data returns null.

  • 11:35 Enable the degrade query database switch on the interface, and complete the commodity picture to the cache

  • 11:36 Problem solved

  • 12:30 Review the code and find the problem

[Cause of failure]

  • Commodity data is rebrushed due to the image size adjustment type. In the process of refreshing, multi-threaded parallel processing is opened in order to shorten the time. In the process of processing, threads are opened according to the number of stores under the store, and sub-threads are opened according to the batch 50 skU picture refreshing under the store.
  • The main thread and child threads are from the same thread pool, and there is no thread pool isolation.
  • Master and child threads occupy each other, causing each other to wait.
  • As the number of tasks increases, the number of newly generated child threads gradually reaches the maximum number of threads and enters the waiting queue
  • The wait queue gradually reaches its maximum against the backlog of tasks
  • The threading policy is rejected, that is, the task is lost
  • Image refresh tasks start to be lost, resulting in missing online cache data

【 summary 】

Let’s review the basics of thread pools.

Important thread pool parameters:

  1. CorePoolSize Specifies the number of core threads. When the number of threads is less than corePoolSize, a thread is created to execute a runnable
  2. MaximumPoolSize Specifies the maximum number of threads. If the number of threads >= corePoolSize, runnable is added to the workQueue
  3. KeepAliveTime keepAliveTime. Maximum keepAliveTime for idle threads that are larger than corePoolSize.
  4. Unit Time unit
  5. WorkQueue Holds the blocking queue of tasks
  6. ThreadFactory Factory for creating threads
  7. Handler rejection policy

Task execution sequence:

  1. When the number of threads is less than corePoolSize, a thread is created to execute the task.
  2. If the number of threads is greater than or equal to corePoolSize and the workQueue is not full, it is added to the workQueue
  3. The number of threads is greater than or equal to corePoolSize, and when the workQueue is full, a new task is created to run, and the total number of threads is smaller than maximumPoolSize
  4. Handler rejectedExecution is executed when the number of threads equals maximumPoolSize and the workQueue is full. That’s the rejection strategy.

ThreadPoolExecutor has four rejection policies by default:

  1. ThreadPoolExecutor.AbortPolicy()An exception is thrown directly RejectedExecutionException
  2. ThreadPoolExecutor.CallerRunsPolicy()Call the run method directly and block execution
  3. ThreadPoolExecutor.DiscardPolicy()Discard subsequent tasks
  4. ThreadPoolExecutor.DiscardOldestPolicy()The task discarded at the head of the queue

Next, we will reproduce the cause process of the accident through a demo

public class FuatureTaskDemo2 { private static ThreadPoolExecutor mExecutor = new ThreadPoolExecutor( 4, 4, 10L, TimeUnit.SECONDS, new LinkedBlockingDeque<>(2), Executors.defaultThreadFactory(), new ThreadPoolExecutor.DiscardPolicy()); /** * @return */ public void getWorker(String name) throws Exception {system.out.println (" execute "+name+" start "); for(int i=0; i<10; i++){ int finalI = i; mExecutor.submit(new Runnable() { @Override public void run() { try { Thread.sleep(100L); } catch (InterruptedException e) { e.printStackTrace(); } system.out.println (" execute "+name+" neutron thread :"+ finalI); }}); } int getActiveCount = (mExecutor).getActiveCount(); int getCorePoolSize = (mExecutor).getCorePoolSize(); int getMaximumPoolSize = (mExecutor).getMaximumPoolSize(); long getTaskCount = (mExecutor).getTaskCount(); BlockingQueue<Runnable> blockingQueue = (mExecutor).getQueue(); System.out.println("getActiveCount"+getActiveCount); System.out.println("getCorePoolSize"+getCorePoolSize); System.out.println("getMaximumPoolSize"+getMaximumPoolSize); System.out.println("getTaskCount"+getTaskCount); System.out.println("blockingQueue"+blockingQueue.size()); mExecutor.shutdown(); } public static void main(String[] args) { FuatureTaskDemo2 it = new FuatureTaskDemo2(); FuatureTaskDemo2 it3 = new FuatureTaskDemo2(); Try {it3.getWorker(" parent thread "); It. GetWorker (" child thread "); } catch (Exception e) { e.printStackTrace(); }}}Copy the code

Execution Result:

Implications for us:

  • As you can see in the demo above. Only six of the 30 child thread tasks were finally executed, and all the rest were rejected.
  • We try to separate the master and child thread pools when executing the core data threads
  • The core mission rejection strategy must beThreadPoolExecutor.CallerRunsPolicy()Call the run method directly and block execution, orThreadPoolExecutor.AbortPolicy()Throw an exception and retry.