单例 Bean 如何为并发请求提供服务?

我有一个关于单例bean如何详细处理并发请求的问题。

我已经在StackOverflow上搜索了这个问题。这是来自堆栈溢出的示例链接,但我只找到了高级细节。我想要有关单例Bean如何处理并发请求以及系统处理器如何查看这些请求的完整详细信息。

我已经研究了在线系统处理器中的并发请求处理。他们说处理器本身有一个调度程序,调度程序将决定处理哪个请求。

好吧。假设我有多个核心处理器,调度程序如何处理并发请求?

任何人都可以向我解释一下单例Bean将如何为JVM和系统中的并发请求提供服务的分步过程吗?

让我用一个具体的例子来解释。我有一个类,如:Sports

class Sports {
    public void playFootball() {
    }

    public void playVolleyBall() {
    }
}

两个请求进来了。第一个请求正在类 的创建单例实例上执行该方法。同时,另一个请求正在类 的同一创建的单例实例上执行该方法。playFootballSportsplayVolleyBallSports

如何使用单例实例?


答案 1

萨拉万·库马尔,

我理解你提出这个问题背后的动机。在我开始研究编译器之前,我也有一个非常类似的想知道Java虚拟机的内部结构。

首先,你的问题给我留下了深刻的印象。为了解决你的问题,需要有几个区别和理解点。首先:单例模式,有时甚至称为反模式,确保只有一个此类实例可用于JVM(Java虚拟机)。这意味着我们实际上是在应用程序中引入全局状态。我知道你理解这一点,但这只是一个澄清点。

现在是内部。

当我们创建一个类的实例时,我们正在创建一个驻留在JVM共享内存中的对象。现在,这些线程独立执行在这些实例上运行的代码。每个线程都有一个工作内存,其中它保存所有线程之间共享的主内存中的数据。这是对已创建的 Singleton 对象的引用所在的位置。从本质上讲,正在发生的事情是,生成的并代表您创建的单例对象的字节码正在这些线程中的每一个线程上执行。

现在,这种情况如何发生的内部情况如下:

每个 JVM 线程都有一个私有 JVM 堆栈,与该线程同时创建。现在,JVM 有一个在所有 JVM 线程之间共享的堆。堆是运行时数据区域,从中为所有类实例和数组分配内存。堆是在 VM 启动时创建的。当您的线程请求单例实例时,它将指向堆中此单例的字节码所在的引用。它将执行相应的代码。在您的例子中,它将为第一个请求执行第一个方法,为第二个请求执行第二个方法。它之所以能够做到这一点,是因为没有锁或限制阻止编译器将程序计数器指向堆中分配此实例的区域。单例类对 Java 虚拟机的唯一限制是,它在此类的堆中只能有一个实例。仅此而已。除此之外,您可以从方法中引用它100次,编译器将指向相同的字节码并简单地执行它。这就是为什么我们通常希望 Singleton 类是无状态的,因为如果我们有任何线程访问它,我们不希望内部变量因为缺乏并发控制而发生突变。

如果您有任何疑问,请告诉我!


答案 2

理想的单例 Bean 不应保持任何状态。这意味着它不会有任何变量来存储特定于它所服务的请求的任何内容。

因此,单例bean将简单地具有无状态代码(例如控制器方法),可以同时执行多个请求,而不会出现任何并发问题。

例如,如果下面是您的单例 Bean:

@Service
public class Calculator {

   public int sum(int a, int b) {
        return a + b;
   } 

}

简单来说,当两个“请求”同时调用Bean的方法时,这意味着该方法将在两个不同的线程中同时执行。因此,它们将拥有自己的执行上下文,这些上下文不会相互重叠。这将安全地允许它们并发运行。sumsum

如果同一个豆子的状态如下:

@Service
public class Calculator {

   int incrementalMultiplier = 0;

   public int mulitply(int a, int b) {
        incrementalMultiplier++;
        return a * b * incrementalMultiplier;
   } 

}

在同时为两个请求提供服务时,这可能会导致问题,因为这是将由两个请求(线程)共享的对象级状态,因此可能会产生意外的结果。incrementalMultiplier

简而言之,无状态单例将能够同时为两个请求提供服务,因为它们将位于不同的线程中。


推荐