单例模式-java

在开发中,我们也许会用到只能实现一个实例的类的情况,也就是面向对象中重要的设计模式——单例模式,下面,就来介绍一下集中常见的单例模式以及优缺点。

  • 饿汉
public class Singleton {
    private Singleton(){}
    private static Singleton singleton = new Singleton();
    public static Singleton getInstance(){
        return singleton;
    }
}

这种实现的额方式,如其名字一般,在开始就创建一个实例,需要的时候调用,有着线程安全的特点,但是存在一些问题,就是浪费资源,如果某个单例模式用的不多,那么每次创建的实例就是浪费了。针对这个特点,就有了懒汉模式。

  • 懒汉模式
public class Singleton {

    private Singleton(){}

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

这是懒汉的实现形式,通过对比可知,此模式只在使用的时候才会创建实例,并赋予用户使用。看起来貌似很完美了,节省了资源。但是在实际的使用中,会遇到多线程的问题,此时有可能创建可不止一个实例,因此,这种实现方式是不安全的。针对此,可以使用java中的锁机制。

  • 懒汉优化——java锁
public class Singleton {

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

}

此实现方式用到了锁机制,在一定程度上保证了线程的安全。但是咱们按照调试bug的思路自己过一遍,假如有两个线程,都通过了if语句,将要执行synchronized 方法内的代码,但是此时只能第一个执行(先来的),于是第一个执行完毕,第二个继续执行,由此可见,第二个线程也执行了synchronized 中的代码,因此,这种方式并没有完美的解决多线程的问题。于是,针对这种特点,引入了双重锁机制。

  • 懒汉优化——双重锁
public class Singleton {

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

仔细观看上面的实现方式,根据上面的检查方式再过一遍,发现第一个线程执行完毕后,第二个线程会进入第二个if语句,但是没有执行if语句中的内容,一次,解决了在多线程的生产环境中的问题。但是,,还要问一句,真的是完美的吗???
引用别人的解释:

不用双重检查锁的单例没有线程安全问题 却有效率问题。 效率在于每次来的都要得到锁。
用双重检查锁的单例有线程安全问题 效率好一些。 线程安全问题在于 讲一个没有完全实例化的对象释放出去。
public static Singleton getInstance()
{
if (instance == null)//4
{
synchronized(Singleton.class) { //1
if (instance == null) //2
instance = new Singleton(); //3
}
}
return instance;
这段代码 中,一个线程看到第四步 发现 不为null,拿去使用了。但是实际是这个不为null的对象,并没有完全实例化。
原因在于: 3处
这个地方代码只有一行,真正意义上完成一个对象需要三步。
1,分配对象的内存空间 1买了房子
2,为该对象的属性赋值 2给房子装修
3,初始化该对象。 3 把名字给你。
这个顺序不一定,加入说 3 执行了 再执行2 就出问题了.
解决办法是 给instance的修饰加上volatile 。这上这个字后 保证不会重排序。。。。这样得到的就是正确的对象。

  • 懒汉优化——volitile
public class Singleton {

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

按照大神的理论,这样就没问题了,,但是细心的我们可以发现,代码非常繁琐,,那么性能怎样呢,,明确的告诉你,并不高,尽管他已经实现了线程安全和懒汉,下面介绍一个更好的。

  • 懒汉——java内部类
public class Singleton {

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

}

相对来说,这才是最好的单例模式实现形式。

你可能感兴趣的:(Java基础知识,设计模式)