Loading

设计模式之单例模式

学习路上,还是得不断的接收新的知识。因为大多数知识都是共通的,这不,学着学着还是得绕到设计模式上,就从最简单的单例模式开始学习吧。基本的模式就有23种,我想写成一个系列的文章,但现在的我肯定是还没有这个能力的。要深刻理解设计模式,最好的方式应该 就是去应用它吧,相信等以后慢慢积累沉淀,我一定会把这个系列写完的==。


1.设计模式是什么

设计模式,其实就是一种设计理念,无关乎语言,使用它可以做到代码复用,保证代码的可靠性。但我现在压根也理解不到它有多大作用。emmm,但慢慢积累就好,毕竟谁也不能一蹴而就,好书也都值得我们去看第二遍第三遍。

就跟阿里巴巴的孤尽说的,学习设计模式的最高境界就是扫地僧,完全忘记设计模式,但写出来的都是设计模式。


2.单例模式

确保一个类只有一个实例,并提供一个全局访问点。

一般使用一个私有构造函数,一个私有静态变量,一个共有的静态函数实现。私有的构造函数保证不能通过构造函数来创建对象实例,只能通过公有的静态函数来返回唯一的私有静态变量。
这里写图片描述


3.单例模式的实现

(1)饿汉式——线程安全

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

因为使用的static修饰唯一的私有变量,所以不管怎么样,获取到的都是这一个单例。

public class Test {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        System.out.println(s1 == s2);
    }
}

输出

true

缺点:不管你是否要使用该实例,在类加载的时候,都会进行实例化(这应该也是饿汉这个名字的由来吧哈哈),这在一定程度上就浪费了资源。


(2)懒汉式——线程不安全

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

这样做的好处是延迟实例化,在没有使用到该类的时候,就不会实例化instance,从而节约资源。缺点是线程不安全,如果有多个线程同时进入if(instance == null),那么就会多次实例化instance。


(3)懒汉模式—–线程安全

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

通过给getInstance方法加一个锁synchronized ,就能保证在一个时间点只能有一个线程进入该方法,从而避免了多次重复实例化的问题。但当一个线程进入的时候,其他线程必须等待,因此在性能上有一定的损耗。


(4)双重校验锁—–线程安全

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

其实我们现在只是要保证instance只被初始化一次就可以使用,所以在instance为null的时候,才给它加锁。如果没有最里层的if判断,当两个线程都进入之后,还是会被初始化两次,只是时间问题,所以最里层的if也是需要的。

我们给静态变量加了volatile 进行修饰,它可以禁止JVM的指令重排。instance = new Singleton();这条语句其实分三步执行:

  1. 分配内存空间

  2. 初始化对象

  3. 将对象指向分配的内存空间

但JVM有时为了优化,会自动进行指令重排,变成1->3->2这样的顺序,在这种情况下,如果是多线程,那么第二个线程获取到的,可能就是一个没有被初始化的对象。所以使用volatile修饰,可以保证在多线程情况下正常运行。


第一篇的总结就到这里啦。

posted @ 2018-05-12 13:11  CodeTiger  阅读(20)  评论(0编辑  收藏  举报