This article is participating in “Java Theme Month – Java Debug Notes Event”, see < Event link > for more details.

preface

  • The development phase of the project is over, and the test phase is going well and the reverse is working. The basic process functions are navigable
  • The next test started performance testing with a goal of 5000 concurrency for the interface, since our project was a traditional one and didn’t need to support that much concurrency. But when I hit 300, my interface started reporting errors.
  • Again, it’s a functional experimental product. Being able to resist concurrency is truly online. Quantitative change causes qualitative change

Business phenomenon

  • There is A place in the project that calls interface A, and you can operate on the page without any problems. Data can be obtained normally
  • When pressure measurement tools such as Jmeter are used for pressure testing, abnormal interface changes may occur. It’s also a ghostly question. Sometimes it’s normal and sometimes it’s not

Problem analysis

  • After layer by layer tracing and breakpoint debugging of IDEA, the problem method was finally located. This code looks fine at first glance
  • When I first saw this code I thought the configuration file was in the wrong place. But then I realized that if it wasn’t in the right place it wouldn’t work at all. So the problem is in the code.

  • A quick review shows that we initialize resultMap each time we get the parameter set in the configuration file. This resultMap is where you store the result set,
  • Now that I’ve got itapplication.ymlAnd our project is not connectedspringcloud configModule so why does it need to be reloaded every time? The key is that there is no lock for each load
  • The fact that there is no lock causes thread A to read and complete the assignment and before returning, thread B executes the method again to empty the resultMap. In this case, thread A has been installed in the configuration file, but when fetching parameters is empty, an error is reported.
  • This is typically the case when developers fail to master concurrency during development. Or not thinking about concurrency. And I’m the victim of wasting my precious time with this problem

The solution

  • Since the problem is caused by concurrency. Then locking is the simplest solution.
  • But you have to deal with it on a case-by-case basis. And locking is not necessarily the answer. Locking can cause performance problems.

Plan a

  • First of all, without locking, we can completely changeresultMap = new HashMap<>();This code can be removed to solve the problem. Because no longer will be emptied
  • The problem is that each fetch performs the loading process. It’s such a waste

Scheme 2

  • Then there’s locking. And out of the empty operation and willresultMapvolatileModification. This avoids the flush operation and ensures that the lock is executed sequentially and guaranteedresultMapThe thread is visible
  • To this, we need to add another layer of judgment
if (resultMap.size() > 0) {
    return resultMap;
}
Copy the code

conclusion

  • Qualitative change causes quantitative change and that’s something we developers have to consider. Concurrency is what advanced programmers do.

Thank you for your thumb up