同时调用多次方法的问题

时间:2018-12-12 20:31:11

标签: java singleton

我有此方法

@RequestMapping(value = "/affaires",
        method = RequestMethod.POST,
        produces = MediaType.APPLICATION_JSON_VALUE)
    @Timed
public ResponseEntity<Affaire> createAffaire(@RequestBody Affaire affaire, HttpServletRequest request) throws URISyntaxException {
  Long idMax = affaireRepository.getMaxId();
  affaire.setReferenceAffaire("AF_"+LocalDate.now().getYear()+" - "+idMax);
  Affaire result = affaireRepository.save(affaire);

我的问题:在我的数据库中,我有2个具有相同ReferenceAffaire的代理人。

因此,我猜想该方法在同一时间被两次调用,并且idMax每次都相同。 有没有一种方法可以避免在同一时间多次调用此方法?

2 个答案:

答案 0 :(得分:1)

在函数声明中添加synchronized。这样可以完全避免您想要的东西:希望同时调用此函数的线程被放入队列中并一个接一个地执行

public synchronized ResponseEntity<Affaire> createAffaire(@RequestBody Affaire affaire, HttpServletRequest request) throws URISyntaxException {

在此处查看documentation

答案 1 :(得分:1)

Servlet是线程化的

根据定义,

Servlet是线程环境。每个请求都在一个线程上处理,因此许多请求意味着许多线程。但是即使两个请求(两个线程)也带来了并发问题的风险。因此,每个servlet程序员都必须编写线程安全的代码

  

所以我猜想该方法在完全相同的时间被调用了两次,并且每次idMax都相同。

是的,完全有可能。而且,如果您看到重复的数字被分配,这确实是可能的原因。

synchronized

任何未构建为线程安全的共享资源都必须受到保护。在您的特定情况下,这意味着affaireRepository.getMaxId()方法需要保护。一种实现方法是synchronized。但是我们必须了解更多信息才能提出最佳建议。

任何编写servlet代码的人都应该阅读,研究和重新阅读Brian Goetz等人的书,Java Concurrency in Practice

  

有没有一种方法可以避免在同一时间多次调用此方法?

不。

在Java Servlet规范的早期版本中,有一个选项可以告诉Servlet容器以单线程模式运行特定的Servlet。如果该Servlet是唯一调用affaireRepository.getMaxId()的地方,那么单线程模式确实会阻止同时调用该方法。但是此功能在规范的更高版本中已删除。单线程破坏了Java Servlet技术所承诺的性能。我不记得了,可能还有其他问题。

同样,如果您正在编写访问非线程安全资源的任何Servlet代码,则您必须了解并发性,并研究synchronizedvolatile, Java内存模型,执行程序框架,避免了Timer,Java EE的JSR 236并发实用程序和其他问题。上面提到的书涵盖了大多数内容以及关键概念。

并发性和线程安全性很复杂,学习起来很困难,并且编写起来很棘手。但这当然是可能的。现代Java为此类工作提供了一些业界最佳的工具。

UUID

作为记录的替代标识符,请考虑使用Universally Unique Identifier (UUID)。就生成ID值而言,线程和并发不会成为问题。


顺便说一句,您的代码还有其他问题。

时区

调用LocalDate.now()隐式使用JVM的当前默认时区。该默认值可以随时更改。因此,如果区域在元旦前后变化,那么您可能最终会无序分配给上一年的序列号。

相反,请始终明确指定所需的时区。

LocalDate.now( ZoneOffset.UTC ) ;