计模式-创建型-单例模式

  单例模式是面试的时候问得比较多的一种设计模式,这个设计模式比较简单,但其实要真的实现单例,并且对JVM比较友好的话,可能需要注意几点。

  单例模式,就是保住在内存中,类的对象唯一。我们知道,用new关键就会产品一个新的对象。要保证内存中只有一个对象,则不能在外部用new关键字进行对象的新建,只能用类调用static静态方法,来获取对象。这个对象不能在外部创建,则只能在类中进行创建

  懒汉式

public class Singleton {
    private static Singleton singleton = null;
    private Singleton() {}
    public static Singleton getSinleton() {
        if(singleton==null) {                      
            singleton = new Singleton();           
        }
        return singleton;
    }
}

  对多线程了解就会知道,在多线程的情况下,这个方式无法保证在内存只生产一个对象。当两个线程都运行到if(singleton==null) 时,会产生两个Singleton 的实例对象。

懒汉式(升级)

public class Singleton {
    private static Singleton singleton = null;
    private Singleton() {}
    public  static synchronized Singleton getSinleton() {
        if(singleton==null) {                      
            singleton = new Singleton();           
        }
        return singleton;
    }
}

  这种方式只是在生产单例的方法上面加上了synchronized,同步方式。每次访问单例的实例是,都需要使用同步方式,效率不高。若单纯的将同步方法改成同步代码块,存在同样的问题。

懒汉式(再升级)

public class Singleton {
    private static Singleton singleton = null;
    private Singleton() {}
    public  static  Singleton getSinleton() {
        if(singleton==null) {
            synchronized(Singleton.class) {
                if(singleton==null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}

  这种模式,只有在第一次访问单例的实例对象是,才会对Singleton类进行加锁,优化了上面的程序。

  这种方法就完美的结果了上面的问题,但是情况真的是这样吗?

  由于程序的无序写,当一个线程运行到singleton = new Singleton()是,存在两个步骤,这行其实做了两个事情:1、调用构造方法,创建了一个实例。2、把这个实例赋值给instance这个实例变量。可问题就是,这两步jvm是不保证顺序的。也就是说。可能在调用构造方法之前,instance已经被设置为非空了。由于无序写,若只完成了singleton非空(未调用构造函数,没有创建实例对象),这是线程退出,另一个线程进入时,创建实例;原来的线程再完成调用构造函数,赋值,则也无法保证内存中对象单一(引用https://blog.csdn.net/u011499747/article/details/48194431)。

懒汉式(那就再升级罗)

public class Singleton {
    private static Singleton singleton = null;
    private Singleton() {}
    public  static  Singleton getSinleton() {
        if(singleton==null) {
            synchronized(Singleton.class) {
                Singleton temp = singleton;
                if(temp==null) {
                    temp = new Singleton();
                }
                singleton = temp;
            }
        }
        return singleton;
    }
}

  好了,这样也避免了无序写的问题了,这就是一个简单的单例模式了。一般程序到这一步就可以了,当然,程序根据反射和序列化其实也可行创建多个对象的,这个后续会说明。

  好了,一个懒汉式单例模式现在就不过多地写了,现在谈一下饿汉式。

  饿汉式

public class Singleton {
    //单例变量 ,static的,在类加载时进行初始化一次,保证线程安全
    private static Singleton singleton = new Singleton();
    private Singleton() {};
    public static Singleton getSingleton() {
        return singleton;
    }
}

  简单,没有多线程问题,爽。不过总能骨头里面挑刺。要是构造的单例很大,构造完又迟迟不使用,会导致资源浪费。

饿汉式(内部实现懒汉式)

public class Singleton {
    public static class SingletonHolder{
        private static Singleton singleton = new Singleton();
    }
    
    private Singleton() {};
    public static Singleton getSingleton() {
        return SingletonHolder.singleton;
    }
}

  哎呀,终于有可以轻松下了。

  单例模式设计完成。

  其实要挑刺,总是能挑的,后面会再次对单例模式进行挑刺,后面将继续说明。

猜你喜欢

转载自www.cnblogs.com/sxrtb/p/10909187.html