Fork me on GitHub

设计模式-单例模式

单例模式就是在访问某个类的时候它只有唯一的实例

单例模式有很多好处,它能够避免实例对象的重复创建,不仅可以减少每次创建对象的时间开销,还可以节约内存空间;能够避免由于操作多个实例导致的逻辑错误。如果一个对象有可能贯穿整个应用程序,而且起到了全局统一管理控制的作用,那么单例模式也许是一个值得考虑的选择。

java常见的单例模式

1.饿汉单例

    饿汉单例模式,顾名思义,就是类加载的时候实例化一个对象出来

    代码实现:

      

public class Singleton{  
    private static final Singleton instance = new Singleton();  
    private Singleton(){}  
    public static Singleton newInstance(){  
        return instance;  
    }  
}  

  优点:类构造为private,保证了外部类不能构造它

       在类加载的时候创建好一个实例,不存在多线程创建多个实例的问题

  缺点:初始化的时候构造,如果该单例未被使用,浪费内存资源

2.懒汉单例

  懒汉单例模式解决了饿汉单例模式的缺点,在使用的时候再对它进行实例化

  代码实现:

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

    由于懒汉单例模式并没有考虑线程安全,故加synchronized🔒,来保证线程安全

    优点:按需创建,节约资源

    缺点:加了线程锁,多线程环境初始化的时候性能不好

3.登记单例(spring单例实现是登记单例和双重锁的实现)

 4.双重校验锁

    为解决加锁懒汉单例引起的性能问题,出现了双重校验锁单例,

    代码实现如下:

public class Singleton {  
    private static Singleton instance = null;  
    private Singleton(){}  
    public static Singleton getInstance() {  
        if (instance == null) {  
            synchronized (Singleton.class) {  
                if (instance == null) {//2  
                    instance = new Singleton();  
                }  
            }  
        }  
        return instance;  
    }  
} 

    优点:在懒汉模式的基础上,解决了多线程并发的性能问题

    缺点:java指令重排优化引起错误,

指令重排:

      重排优化是指在不改变原语义的情况下,通过调整指令的执行顺序让程序运行的更快。JVM中并没有规定编译器优化相关的内容,也就是说JVM可以自由的进行指令重排序的优化。这个问题的关键就在于由于指令重排优化的存在,导致初始化Singleton和将对象地址赋给instance字段的顺序是不确定的。在某个线程创建单例对象时,在构造方法被调用之前,就为该对象分配了内存空间并将对象的字段设置为默认值。此时就可以将分配的内存地址赋值给instance字段了,然而该对象可能还没有初始化。若紧接着另外一个线程来调用getInstance,取到的就是状态不正确的对象,程序就会出错。

  解决方案:加volatile,加volatile的一个语义是禁止指令重排序优化,也就保证了instance变量被赋值的时候对象已经是初始化过的,从而避免了上面说到的问题

  volatile详解

    首先我们要先意识到有这样的现象,编译器为了加快程序运行的速度,对一些变量的写操作会先在寄存器或者是CPU缓存上进行,最后才写入内存.
    而在这个过程,变量的新值对其他线程是不可见的.而volatile的作用就是使它修饰的变量的读写操作都必须在内存中进行!

5.静态内部类

    代码实现:

public class Singleton{  
    private static class SingletonHolder{  
        public static Singleton instance = new Singleton();  
    }  
    private Singleton(){}  
    public static Singleton newInstance(){  
        return SingletonHolder.instance;  
    }  
}  

 

    优点:内部类自己实现,保证延迟加载,保证线程安全

 

posted @ 2017-09-28 13:11  devass  阅读(148)  评论(0编辑  收藏  举报