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

bei*_*man 38 java multithreading

我有多个线程在运行,它访问singleton对象并调用它的方法并在其中传递对象.在方法中,我只对收到的对象进行一些计算.我听说在这种情况下没有任何问题,因为它是无国籍的,而且对所有人都是免费的.

我的问题是它是如何免费的?我想知道多个线程如何在自己的线程中调用共享方法而不覆盖其他线程的传递对象?请明确内存分配和堆栈级别.

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
    }

}
Run Code Online (Sandbox Code Playgroud)

Xtr*_*ica 60

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

Singleton对象中,您有:

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

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

此外,如果您编写一个Thread.currentThread().getId();基本上返回您正在执行单线程方法的线程ID,您将获得不同的ID,因为不同的线程正在执行自己的方法堆栈.作为无国籍意味着你已经没有字段到要在他们之中共享单身!

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

无状态意味着bean没有任何可修改的字段可供共享.这意味着您的对象中只有方法或/和静态内容,因此您可以在任何地方使用它们并始终返回相同的结果.您不必担心同步对该字段的访问.

这是关于无状态的基本示例,假设您有一个只执行求和操作的类:

public class StatelessClass{

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

}
Run Code Online (Sandbox Code Playgroud)

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

public abstract class StatelessClass{

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

}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用它StatelessClass.sum(1,1);,这实际上非常类似于拥有一个Singleton对象本身,区别在于在Singleton中你有一个在应用程序中共享的唯一实例.

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

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

}
Run Code Online (Sandbox Code Playgroud)

但是,具有可修改的字段会使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;
    }

}
Run Code Online (Sandbox Code Playgroud)

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

所有这一切都还正常在解释这个答案的Threadsafety一块由@BalusC.

  • @Xtreme Biker 完美解释了..非常感谢 (2认同)
  • @XtremeBiker我有一个疑问,你能解释一下spring如何处理这个功能吗?"每个线程都会执行Singleton对象的代码部分,包含在自己的执行中." spring如何用不同的线程共享它的对象实例? (2认同)

Yur*_*ula 5

你可以用"披萨术语"来思考它.你去了披萨咖啡馆,其他几个人也和你一起进去了.每个人都下了订单,然后坐到自己的桌子旁.然后到达披萨盒,你们每个人都开始吃自己的饭.

在这里,咖啡馆是单身,披萨男孩是CPU核心,你和其他客户都是线程,订单和披萨分别是输入和输出数据,表是一块内存.

正如您所看到的,每个线程都有自己的内存,因此CPU可以区分您的数据并且不会混淆.当你问到堆栈时,它不是一个重要的参与者,因为每个线程都有它自己的堆栈(作为你的内存块的一部分).


Nar*_*hai 5

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

为什么线程安全?

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

注意:你可以说你的这个Singleton是线程安全的但不是应用程序是线程安全的,因为它还取决于Object o传递的是否本身是线程安全的.但就Singleton的安全性而言,它是线程安全的.