正确使用Singleton Pattern

时间:2012-11-12 21:09:23

标签: java multithreading design-patterns synchronization singleton

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

这是我的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());

    }       
}

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

  

Main:0 Thread:1 Thread:1

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

7 个答案:

答案 0 :(得分:8)

您应该避免同步getInstance方法(因为只是为了在初始化后获取实例,这只是一个不必要的开销)。以下是懒惰初始化单例的推荐方法:

public class OurSingleton {

    private OurSingleton() { }

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

    private static class Holder {
        private static OurSingleton instance = new OurSingleton();
    }        
}

答案 1 :(得分:5)

  

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

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

在Java中实现Singleton Pattern的最简单,最安全的方法是使用枚举:

public enum MySingleton {
    INSTANCE;

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

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

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

答案 2 :(得分:4)

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

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

答案 3 :(得分:2)

只有一个OurSingleton实例,但类getSingletonInstance(使用适当的CAPS!)本身并不是单例。这是你放置柜台的那个。

答案 4 :(得分:2)

我会这样解释:

当您加载类OurSingleton时,静态计数器getInstanceCounter被初始化为零。然后,有多少次您获得该类的新实例,计数器始终为1,表示您确实是一个单身人士。

我建议进行以下更改

  1. 将Singleton中的静态变量设为私有:ourSingletongetInstanceCounter
  2. 删除方法上的synchronized关键字,因为这是一个不必要的开销

答案 5 :(得分:2)

请看另一个答案,了解如何做得更好,对于你的问题,为什么要得到这个输出

Main: 0 
Thread: 1
Thread: 1

代码

static int getInstanceCounter;

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

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

没有创建OurSignleton的实例,因此mySignleton.getCounter()仍为零。

运行其中一个或两个线程将导致创建一个OurSingleton实例,并且getInstanceCounter将为一个。

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


除了一些关于你的代码的东西,它可能看起来很挑剔,但会帮助其他人阅读你的代码。

  • 请变量私有

    static int getInstanceCounter; =&GT;
    private static int getInstanceCounter;

  • 不应使用get

    命名变量

    private static int getInstanceCounter; =&GT;
    private static int instanceCounter;

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

    mySingleton.getCounter()=&gt;
    OurSingleton.getCount()

  • 这也意味着永远不应该引用从未赋值的变量mySingleton,应该删除它。

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

答案 6 :(得分:0)

通过OurSingleton.getInstanceCounter(静态成员函数)对OurSingleton.getCounter ()(静态成员)的访问不同步。这是唯一重要的事情。你的程序的其余部分简直使事情复杂化。请注意,在main中,您通过mySingleton.getCounter ()null指针上调用静态函数! mySingleton永远不会有null以外的其他值。

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