1

我正在尝试实现单例模式的示例。我们的问题之一是运行两个线程,每个线程都调用 getInstance() 并验证只创建了 Singleton 对象的一个​​实例。

这是我的单例代码;

public class OurSingleton {

    static OurSingleton ourSingleton;
    static int instanceCounter;

    private OurSingleton(){
        instanceCounter++;
    }

    public static synchronized OurSingleton GetSingletonInstance(){

        if( ourSingleton == null){

            ourSingleton = new OurSingleton();

        }
        return ourSingleton;    
    }

    public static int getCounter() {

        return instanceCounter;

    }
}

还有我的主要;

public class Main {

    /**
     * @param args
     */
    public static void main(String[] args) {

        OurSingleton mySingleton = null;

        Thread one = new Thread(new GetSingletonInstance(mySingleton));
        Thread two = new Thread(new GetSingletonInstance(mySingleton));

        one.start();
        two.start();


        System.out.println("Main: " + mySingleton.getCounter());
    }   
}

class GetSingletonInstance implements Runnable {

    int count = 0;
    OurSingleton singleton;

    public GetSingletonInstance(OurSingleton ourSingleton){
        singleton = ourSingleton;
    }

    @Override
    public void run() {
        try {
            while (count < 5000000) {
                singleton.getSingletonInstance();
                count++;    
            }

        } catch (Exception e) {

            e.printStackTrace();
        }

        System.out.println("Thread: " + singleton.getCounter());

    }       
}

当我运行此代码时,我得到以下输出;

主要:0 线程:1 线程:1

有人可以解释这个输出的原因吗?我以为只有一个 Singleton 实例存在。这是否意味着正在线程中创建另一个对象?任何建议表示赞赏!

4

7 回答 7

8

您应该避免同步该getInstance方法(因为它只是在初始化实例后获取实例的不必要开销)。以下是延迟初始化单例的推荐方法:

public class OurSingleton {

    private OurSingleton() { }

    public static OurSingleton getInstance() {
        return Holder.instance;
    }

    private static class Holder {
        private static OurSingleton instance = new OurSingleton();
    }        
}
于 2012-11-12T21:16:21.017 回答
5

以为只有一个 Singleton 实例存在。这是否意味着正在线程中创建另一个对象?

JVM中只存在一个单例实例;您的每个线程都显示只有一个实例的事实。

在 Java 中实现单例模式最简单、最安全的方法是使用枚举:

public enum MySingleton {
    INSTANCE;

    public void doStuffHere() {
        //...
    }
}

public class ClientClass {
    public void myMethod() {
        MySingleton mySingleton = MySingleton.INSTANCE;
        mySingleton.doStuff();
    }
}

您只有一个 MySingleton 实例,它是线程安全的。

于 2012-11-12T21:37:43.963 回答
4

您已经从同一类创建了 2 个线程实例。每个打印单例对象的实例数。数字为 1 表示您确实只创建了一个实例,因此您的单例已正确实现。您打印了两次,因为您创建了 2 个线程。

如果你想避免混淆移动线System.out.println(...)到你的主要方法。

于 2012-11-12T21:15:29.943 回答
2

我会这样解释:

当你加载类OurSingleton时,静态计数器getInstanceCounter被初始化为零。然后你得到类的新实例多少次,计数器总是1,表明你确实有一个单例。

我建议进行以下更改

  1. 将 Singleton 中的静态变量设为私有:ourSingletongetInstanceCounter
  2. 删除方法上的同步关键字,因为它是不必要的开销
于 2012-11-12T21:26:11.580 回答
2

只有一个OurSingleton实例,但类getSingletonInstance(使用正确的大写字母!)本身不是单例。它就是你放置柜台的地方。

于 2012-11-12T21:12:29.077 回答
2

查看其他答案以了解如何更好地做到这一点,对于您的问题,您为什么要获得此输出

Main: 0 
Thread: 1
Thread: 1

用代码

static int getInstanceCounter;

您正在设置声明并将静态变量设置为零。因此,在创建 OurSingleton 的任何实例之前,getInstanceCounter 的值为零。你打电话时

System.out.println("Main: " + mySingleton.getCounter());

没有创建 OurSignleton 的实例,所以 mySignleton.getCounter() 仍然为零。

运行一个或两个线程将导致创建 OurSingleton 的一个实例,而 getInstanceCounter 将是一个。

你的单身人士正在工作。尽管在其他答案中提到了更好的方法。


顺便说一句,关于您的代码的一些事情可能看起来很挑剔,但会帮助其他人阅读您的代码。

  • 请将变量设为私有

    静态int getInstanceCounter;=>
    私有静态 int getInstanceCounter;

  • 变量不应该用 get 命名

    私有静态int getInstanceCounter;=>
    私有静态 int instanceCounter;

  • 不要在类的实例上引用静态方法

    mySingleton.getCounter() =>
    OurSingleton.getCount()

  • 这也意味着永远不应该引用从未分配值的变量 mySingleton 并且应该删除。

    public GetSingletonInstance(OurSingleton ourSingleton){
    singleton = ourSingleton;
    }
    =>
    public GetSingletonInstance(){
    singleton = OurSingleton.getInstance();
    }

于 2012-11-12T22:08:02.507 回答
0

OurSingleton.getInstanceCounter通过 (静态成员函数)访问(静态成员OurSingleton.getCounter ())是不同步的。这是唯一重要的事情。程序的其余部分只是使事情复杂化。请注意,main您是通过在指针mySingleton.getCounter ()上调用静态函数!从来没有比.nullmySingletonnull

您应该使用enum来实现单例模式,或者至少只使用一个静态变量,即持有单例对象的变量。

于 2012-11-12T21:24:26.583 回答