多个线程如何调用单例对象的方法并处理它们?

2022-08-31 17:34:48

我有多个线程在运行,它们访问单例对象并调用其方法并在其中传递对象。在该方法中,我只对收到的对象进行一些计算。我听说在这种情况下不会有任何问题,因为它是无状态的,对所有人都是免费的。

我的问题是它如何对所有人免费?我想知道多个线程如何在自己的线程中调用共享方法而不覆盖其他线程的传递对象?请从内存分配方面和堆栈级别进行解释。

class Singleton class{

    //no shared class or object variables and only one copy of this Singleton object exists.

    Object someMethod(Object o){
        //do some calculation or logic on the object o and return some string result
    }

}

答案 1

我认为你必须区分你已经存储在内存中的内容和代码执行。

单例对象中,您有:

  • 字段:它们存储在内存中。它们可以在多个线程之间共享,并且您不能保证它们将保持一致(除非您使它们同步)。
  • 要调用的方法:可以从多个线程调用它们。每个执行都是独立的,并且是线程安全的,除非它们不正确地访问某些共享字段。

现在来回答你的问题:如果你在多个线程之间共享你的单例对象并同时访问它,那么每个线程都将执行单例对象的代码部分,包装在它自己的执行中。

此外,如果您编写一个基本上返回您正在执行的线程ID到单例方法中的线程ID,您将获得不同的ID,因为不同的线程正在执行它们自己的方法堆栈。无状态意味着你没有字段进入单例在它们之间共享!Thread.currentThread().getId();

关于无国籍和有国籍的一句话

无状态意味着 Bean 没有任何可修改的字段可以共享。这意味着你的对象中只有方法或/和静态的东西,所以你可以在任何地方使用它们,并且总是会返回相同的结果。您不必担心同步对字段的访问。

这是一个关于无状态的基本示例,假设您有一个只执行 sum 运算的类:

public class StatelessClass{

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

}

以同样的方式,你可以将其声明为抽象类(本身没有可实例化),并使其方法静态,这意味着你不需要它的任何实例来调用它的方法:

public abstract class StatelessClass{

    /**
    *   I only sum objects
    */
    public static int sum(int a, int b){
        return a+b;
    }

}

然后,您可以将其用作 ,这实际上与拥有 Singleton 对象本身非常相似,但不同之处在于,在 Singleton 中,您在应用程序中共享了一个唯一实例。StatelessClass.sum(1,1);

以同样的方式,拥有一个注入并提供对服务的访问的字段都不会被视为改变对象的状态:

public class StatelessServiceClass{

    private Service service;

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

    public void setService(Service serv){
        this.service=serv;
    }

}

但是,具有可修改的字段会使 Object 具有状态

public class StatefulClass{

    //This fields make the object STATEFUL
    private int totalSum = 0;

    public int sum(int a, int b){
        int sum = a + b;
        totalSum = totalSum + sum;
        if (totalSum > 100)
            System.out.println("This thread "+Thread.currentThread().getId()+
                +" got it!");
        return sum;
    }

}

由于可以同时由多个线程访问,因此应保证以同步方式访问。除非您这样做,否则打印的句子不能保证是真实的。sumtotalSum

所有这些在@BalusC的Threadsafety文章中也得到了适当的解释。


答案 2

每个线程都有自己的 副本,因此即使多个线程同时调用该方法,Thread 的每个方法调用也将分配自己的堆栈,并且逻辑将在那里应用。Object o

为什么它是线程安全的?

线程堆栈是它们的私有,并且默认情况下是线程安全的,因为没有其他线程可以进入其他堆栈。

注意:你可以说你的这个单例是线程安全的,但不是说应用程序是线程安全的,因为它还取决于所传递的本身是否是线程安全的。但就辛格尔顿的安全性而言,它是线程安全的。Object o