In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-30 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/01 Report--
This article mainly introduces the relevant knowledge of "how to solve the problem of synchronized lock failure under Spring transaction management". The editor shows you the operation process through an actual case, and the operation method is simple, fast and practical. I hope this article "how to solve the problem of synchronized lock failure under Spring transaction management" can help you solve the problem.
Recently I have seen a technical problem: synchronized lock problem?
10000 threads are started, and each thread adds 1 to the money field of the employee table [initial value is 0]. Pessimistic lock and optimistic lock are not used, but the synchronized keyword is added to the business layer method. The problem is that after the code is executed, the money field in the database is not 10000, but less than 10000. What is the problem?
Service layer code:
SQL code (without pessimistic / optimistic locks):
Run the code with 1000 threads:
To put it simply: multithreading runs a method modified with the synchronized keyword, which operates on the database. According to normal logic, the final value should be 1000, but after many tests, the result is less than 1000. Why is that?
I. my thinking
Since the test result is less than 1000, that means this code is not thread-safe. It's not thread-safe, so what's the problem? As we all know, the synchronized method can guarantee the modified code block, and the method can ensure the order, atomicity and visibility.
It is reasonable that the above code runs, and the increaseMoney () of the Service layer in the question is ordered, atomic, and visible, so it is concluded that it has nothing to do with synchronized.
Since you can't find the cause at the Java level, analyze it at the database level (because the database is being manipulated within the method). The @ Transcational annotation is added before the increaseMoney () method, indicating that the method is transactional. A transaction ensures that the SQL of the same group either succeeds or fails at the same time. It is reasonable to say that if there is no error, each thread should have + 1 for the value of money. In theory, the result should be 1000.
Based on the above analysis, I suspected that the questioner did not hhhh well, so I also ran to test it and found that there was a real problem with using it in the way of the questioner.
First, post my test code:
@ RestControllerpublic class EmployeeController {@ Autowired private EmployeeService employeeService; @ RequestMapping ("/ add") public void addEmployee () {for (int I = 0; I)
< 1000; i++) { new Thread(() ->EmployeeService.addEmployee (). Start ();}} @ Servicepublic class EmployeeService {@ Autowired private EmployeeRepository employeeRepository; @ Transactional public synchronized void addEmployee () {/ / find the record with ID 8, and then increase the age by one Employee employee = employeeRepository.getOne (8); System.out.println (employee); Integer age = employee.getAge (); employee.setAge (age + 1) EmployeeRepository.save (employee);}}
Simply print the employees value you get each time, and get the order in which SQL is executed, as follows (post a small portion):
As follows (post a small portion):
From the case of printing, we can see that there is no serial execution of the addEmployee () method in the case of multithreading. This results in repeated changes to the same value, so the final value is less than 1000.
Second, the reasons for the emergence of diagrams
I found that it was not executed synchronously, so I suspected that the synchronized keyword and Spring must be in conflict. So I searched for these two keywords and found the problem.
We know that the underlying layer of Spring transactions is Spring AOP, while the underlying layer of Spring AOP is dynamic proxy technology. Let's review the dynamic agent:
Public static void main (String [] args) {/ / Target object Object target; Proxy.newProxyInstance (ClassLoader.getSystemClassLoader (), Main.class, new InvocationHandler () {@ Override public Object invoke (Object proxy, Method method, Object [] args) throws Throwable {/ / any method with @ Transcational annotation will be blocked / / 1. Open transaction method.invoke (target); / / 2. Commit transaction return null;}});}
In fact, Spring does the same thing as above. We can take a look at invokeWithinTransaction () in the TransactionAspectSupport class:
Open the transaction before calling the method, and commit the transaction after calling the method
In a multithreaded environment, it may occur: the method execution (synchronized code block execution is finished), the transaction has not been committed, other threads can enter the method modified by synchronized, and then read the data that has not yet committed the transaction, this data is not up-to-date, so this problem occurs.
Third, solve the problem
From the above, we can see that the problem is that the @ Transcational annotation is used with synchronized, and the scope of locking does not cover the entire transaction. So we can do this:
Create a new class called SynchronizedService and have it call the addEmployee () method. The whole code is as follows:
@ RestControllerpublic class EmployeeController {@ Autowired private SynchronizedService synchronizedService; @ RequestMapping ("/ add") public void addEmployee () {for (int I = 0; I)
< 1000; i++) { new Thread(() ->SynchronizedService.synchronizedAddEmployee () .start ();} / the newly created Service class @ Servicepublic class SynchronizedService {@ Autowired private EmployeeService employeeService; / / synchronize public synchronized void synchronizedAddEmployee () {employeeService.addEmployee ();}} @ Servicepublic class EmployeeService {@ Autowired private EmployeeRepository employeeRepository @ Transactional public void addEmployee () {/ / find the record with an ID of 8, and then increase the age by one Employee employee = employeeRepository.getOne (8); System.out.println (Thread.currentThread (). GetName () + employee); Integer age = employee.getAge (); employee.setAge (age + 1); employeeRepository.save (employee);}}
We include the scope of synchronized locks on the entire Spring transaction, so that there is no thread safety problem. When testing, we can find that 1000 threads run much slower than before, and of course our data is correct:
Putting aside the synchronized failure caused by the above transactions, synchronized itself is a pessimistic lock with a high cost, such as the thread safety problem of database data modification, you can use optimistic locks, add version fields to the table, compare the expected value with the database value each time, and spin a certain number of attempts to modify it if it fails, and version+1 if the modification is successful.
This is the end of the content on "how to solve the problem of synchronized lock failure under Spring transaction management". Thank you for your reading. If you want to know more about the industry, you can follow the industry information channel. The editor will update different knowledge points for you every day.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.