55

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

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

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
    }

}
4

4 回答 4

76

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

单例对象中,您有:

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

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

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

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

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

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

public class StatelessClass{

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

}

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

public abstract class StatelessClass{

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

}

然后您可以将其用作,这实际上与拥有一个SingletonStatelessClass.sum(1,1);对象本身非常相似,不同之处在于在 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;
    }

}

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

}

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

@BalusC在此答案的Threadsafety文章中也正确解释了所有这些。

于 2013-08-31T11:14:07.840 回答
8

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

为什么它是线程安全的?

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

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

于 2013-08-31T11:17:19.873 回答
6

您可以用“披萨术语”来考虑它。你去了披萨咖啡馆,其他几个人也和你一起进来。每个人都下了订单,然后坐在自己的桌子旁。比萨盒要到了,你们每个人都开始吃自己的饭菜。

在这里,cafe 是一个单例,pizza boy 是 CPU 核,你和其他客户是线程,orders 和 pizza 分别是输入和输出数据,table 是一块内存。

如您所见,每个线程都有自己的一块内存,因此 CPU 可以区分您的数据而不会混淆。正如您所问的堆栈,它在这里并不是一个重要的参与者,因为每个线程都有自己的堆栈(作为内存块的一部分)。

于 2013-08-31T11:17:02.877 回答
0
    import java.util.ArrayList;
    import java.util.Collections;
    import java.util.List;

    public class MainClass {
        public final static int MAX_THREAD = 20;
        public MainClass() {
            // TODO Auto-generated constructor stub
        }

        public static void main(String[] args) {

            List<Singleton> singletonList = new MainClass().createSingletonObjects();
            singletonList = Collections.synchronizedList(singletonList); 

            int index = 0;
            for (int i = 0; i < MAX_THREAD; i++) {

                Thread thread1 = new Thread(new MyThread(singletonList,index), "Thread"+i);
                thread1.start();
                index++;
            if (index == singletonList.size()) {
            index = 0;  
            }

            }

        }

    public synchronized List<Singleton> createSingletonObjects(){
        List<Singleton> listSingleton = new ArrayList<Singleton>();
            listSingleton.add(MySingleton1.getInstance());
            listSingleton.add(MySingleton2.getInstance());
            listSingleton.add(MySingleton3.getInstance());
            listSingleton.add(MySingleton4.getInstance());
            listSingleton.add(MySingleton5.getInstance());

            return listSingleton;

        }

    }


public class MySingleton1 extends Singleton{
    private static Singleton mySingleton;
    private MySingleton1() {
        super();
        // TODO Auto-generated constructor stub
    }

    public static Singleton getInstance() {
        if (mySingleton == null) {
            mySingleton = new MySingleton1();
            return mySingleton;
        }
        return mySingleton;
    }


public class MySingleton2 extends Singleton{
    private static Singleton mySingleton;
    private MySingleton2() {
        super();
        // TODO Auto-generated constructor stub
    }

    public static Singleton getInstance() {
        if (mySingleton == null) {
            mySingleton = new MySingleton2();
            return mySingleton;
        }
        return mySingleton;
    }







}


public class MySingleton3 extends Singleton{
    private static Singleton mySingleton;
    private MySingleton3() {
        super();
        // TODO Auto-generated constructor stub
    }

    public static Singleton getInstance() {
        if (mySingleton == null) {
            mySingleton = new MySingleton3();
            return mySingleton;
        }
        return mySingleton;
    }







}


public class MySingleton4 extends Singleton{
    private static Singleton mySingleton;
    private MySingleton4() {
        super();
        // TODO Auto-generated constructor stub
    }

    public static Singleton getInstance() {
        if (mySingleton == null) {
            mySingleton = new MySingleton4();
            return mySingleton;
        }
        return mySingleton;
    }







}


public class MySingleton5 extends Singleton{
    private static Singleton mySingleton;
    private MySingleton5() {
        super();
        // TODO Auto-generated constructor stub
    }

    public static Singleton getInstance() {
        if (mySingleton == null) {
            mySingleton = new MySingleton5();
            return mySingleton;
        }
        return mySingleton;
    }







}
于 2019-03-03T19:10:46.910 回答