Synchronized Method In Spring MVC
Asked Answered
L

2

9

I am attempting to use synchronize method in spring controller. Because our Payment gateway hits method [@RequestMapping(value="/pay",method=RequestMethod.POST)] different transactions [txn id : txn01 & txn02] at a time. But these 2 different transaction processing one by one than parallel due to using synchronize block.

Problem -> Why i am using synchronize block in controller is that say Transaction [txn01] hits [@RequestMapping(value="/pay",method=RequestMethod.POST)] twice like duplicate call from payment gateway. before finishing first call [backend processing] i get second call from payment gateway for same tran id.

Is there any way to process two different transaction parallel with using transaction id in synchronize block other than duplicate call i mean same tran id. Please advice me.

Please let me know if my question is unclear.

@RequestMapping(value="/pay",method=RequestMethod.POST)
public String payAck(HttpServletRequest httpRequest,HttpServletResponse httpResponse,HttpSession session){
    synchronized (this) {
        return this.processPayAck(httpRequest, httpResponse, session);
    }
}

public synchronized String processPayAck(HttpServletRequest httpRequest,HttpServletResponse httpResponse,HttpSession session){
   // Payment Acknowledgment process here
    if (sametranIDNotExists) {
        // first call here
        callWS(); - processing business logic.
        return someURL;
    } else {
       // Gets second call here before first call completed
       return someURL;
    }
}

Modified code :

Is it correct way to use intern inside synchronize block.

@RequestMapping(value="/pay",method=RequestMethod.POST)
public String payAck(HttpServletRequest httpRequest,HttpServletResponse httpResponse,HttpSession session){
    String tranID = httpRequest.getParameter("tranID");
    synchronized (String.valueOf(tranID).intern()) {
        return processPayAck(httpRequest, httpResponse, session);
    }
}
Lobotomy answered 30/1, 2018 at 12:33 Comment(4)
Your question is quite unclear. I don't get why you're trying to synchronize (you shouldn't have to), and you don't seem to understand how synchronized works anyway (the method is already synchronized, so synchronizing the call to the method is useless). So it seems you're trying a random solution to solve an unclear problem. Remember that even if that solves the problem, it won't solve it anymore as soon as you have two servers instead of one. Your concurrency problem should probably be handled at the database layer (using locks)Mudpack
if txn01 hits /pay more than one time, you won't delete duplicate calls by synchronisation, you will arrange them in a queue - they all are going to pass through this methodTowns
@Andrew.. if txn01 hits more than one time, its should ok in queue. but for different transaction txn01 & txn02 it will be parallelLobotomy
@JB Nizet.. cannot able to handle at database level, since i am using external webservice. Thats why i am using synchronization to avoid duplicate calls, but it affects different transactions.Lobotomy
K
5

I'm not sure if you are working in a distributed environment.

If there is only one machine, you can remove the syncronized keyword and create name-based locks with your transation id instead.

If this program is working in a cluster and there are multiple machines, which means the request might be assigned to different machine, I think you need to aquaire distribution-lock with Redis or other frameworks.

Kulak answered 30/1, 2018 at 12:47 Comment(4)
Can we synchronize by some variable like transaction idLobotomy
@DEADEND You can try this synchronized (String.valueOf(transactionId).intern())Kulak
@DEADEND #134488Kulak
@user27149.. from synchronized (String.valueOf(transactionId).intern()) transaction id should be instance variableor method local variable. somebody not preferring not to use intern in synchronize blockLobotomy
S
2

Synchronized block is used to provide thread safety. Also when multiple threads are trying to access same object, thread only with object level lock can acces synchronized(this) block. While one among a group of threads get object level lock, rest of the threads wait (Threads access synchronised block one by one but not in parallel).

Appropriate use : Use synchronized block when threads are trying modifying same resource(to avoid data inconsistancy). In this case threads are trying to modify same database resource. But as mentioned modifications are done on 2 different transactions(rows).

If modifying one row doesn't harm the other one then it is not required to use the line

return this.processPayAck(httpRequest, httpResponse, session);

within synchronised block. Instead it could be written as:

@RequestMapping(value="/pay",method=RequestMethod.POST)
public String payAck(HttpServletRequest httpRequest,HttpServletResponse httpResponse,HttpSession session){

            return this.processPayAck(httpRequest, httpResponse, session);
}

Suggestion : Use CopyOnWriteArrayList (as an instance variable not local variable) to store transaction id at the end of payAck method and use contains("textId") method to check whether the given transaction id is using payAck method again.

Subplot answered 30/1, 2018 at 13:9 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.