【问题标题】:Synchronized Method In Spring MVCSpring MVC 中的同步方法
【发布时间】:2018-07-09 08:22:02
【问题描述】:

我正在尝试在 spring 控制器中使用同步方法。因为我们的支付网关一次点击方法 [@RequestMapping(value="/pay",method=RequestMethod.POST)] 不同的交易 [txn id : txn01 & txn02]。但是由于使用了同步块,这两个不同的事务处理是一一处理而不是并行处理。

问题 -> 为什么我在控制器中使用同步块是说事务 [txn01] 命中 [@RequestMapping(value="/pay",method=RequestMethod.POST)] 两次,就像来自支付网关的重复调用一样。在完成第一次调用 [后端处理] 之前,我从支付网关收到第二次调用相同的 tran id。

除了重复调用之外,有什么方法可以在同步块中使用事务 ID 并行处理两个不同的事务,我的意思是相同的事务 ID。请给我建议。

如果我的问题不清楚,请告诉我。

@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;
    }
}

修改后的代码:

在同步块中使用实习生的方法是否正确。

@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);
    }
}

【问题讨论】:

  • 你的问题很不清楚。我不明白您为什么要尝试同步(您不应该这样做),而且您似乎也不了解同步的工作原理(该方法已经同步,因此同步对该方法的调用是无用的) .因此,您似乎正在尝试随机解决方案来解决不清楚的问题。请记住,即使这样可以解决问题,只要您拥有两台服务器而不是一台,它就不会再解决问题了。您的并发问题可能应该在数据库层处理(使用锁)
  • 如果txn01多次点击/pay,你不会通过同步删除重复的调用,你会将它们排成一个队列——它们都会通过这个方法跨度>
  • @Andrew.. 如果 txn01 多次命中,它应该可以在队列中。但是对于不同的交易 txn01 & txn02 它将是并行的
  • @JB Nizet.. 无法在数据库级别处理,因为我使用的是外部 Web 服务。这就是为什么我使用同步来避免重复调用,但它会影响不同的事务。

标签: java multithreading spring-mvc synchronized


【解决方案1】:

我不确定您是否在分布式环境中工作。

如果只有一台机器,您可以删除 syncronized 关键字并使用您的事务 ID 创建 name-based 锁。

如果这个程序在一个集群中工作并且有多台机器,这意味着请求可能被分配到不同的机器上,我认为你需要使用 Redis 或其他框架获取distribution-lock

【讨论】:

  • 我们可以通过一些变量来同步,比如事务ID
  • @DEADEND 你可以试试这个synchronized (String.valueOf(transactionId).intern())
  • @user27149.. from synchronized (String.valueOf(transactionId).intern()) 事务 id 应该是实例变量或方法局部变量。有人不喜欢在同步块中不使用实习生
【解决方案2】:

同步块用于提供线程安全。此外,当多个线程试图访问同一个对象时,只有具有对象级锁的线程才能访问synchronized(this) 块。当一组线程中的一个获得对象级锁时,其余线程等待(线程访问同步块一个接一个,但不是并行)。

适当使用:当线程尝试修改相同资源时使用同步块(以避免数据不一致)。在这种情况下,线程正在尝试修改相同的数据库资源。但如前所述,修改是在 2 个不同的事务(行)上完成的。

如果修改一行不会损害另一行,则不需要使用该行

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

在同步块内。相反,它可以写成:

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

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

建议:使用CopyOnWriteArrayList(作为实例变量而不是局部变量)在payAck方法的末尾存储事务id,并使用contains("textId")方法检查给定的事务id是否再次使用payAck 方法。

【讨论】:

    猜你喜欢
    • 2014-04-18
    • 1970-01-01
    • 2016-06-04
    • 2015-06-08
    • 1970-01-01
    • 2020-09-15
    • 1970-01-01
    • 2015-12-10
    • 2017-09-27
    相关资源
    最近更新 更多