设计模式---单例模式

单例模式

单例模式的介绍

保证一个类仅有一个实例,并提供用于一个访问它的全局访问点。

img

单例模式是Java中最简单的设计模式,它的核心在于它能保持全局只有一个这个类的对象,如果外部想要去获取这个类的对象时,不能自己创建,只能通过这个类提供给外部的方法去获取,也正是这个原因,单例模式的类的构造方法一般都是私有的。

五种写法及其优缺点

1.饿汉模式

饿汉模式简单来说就是自己很饿,在类加载的时候就去创建了对象。

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

优点:没有加锁,效率很高

缺点:类加载时就初始化了,浪费内存

2.懒汉模式

懒汉模式就是很懒,只有在获取对象的时候才会去创建对象。

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

优点:只有在使用的时候才会初始化,节约了资源

缺点:第一次加载的时候需要及时进行实例化,反应稍慢,最大的问题是每次调用getInstance方法时都需要线程同步,造成不必要的同步。

3.双重检查模式/DCL

DCL是结合了懒汉模式写的,他相当于是懒汉模式的改良版,既能在调用getInstance的时候才去实例化,又能保证线程安全,最重要的是初始化后调用的时候不再需要加锁。

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

这个方法里面有两个判空,第一个判空是为了避免不必要的同步,第二层判空是为了在null的时候去创建实例,保证线程安全。如果没有第二个判空,则会出现一个问题:假设我有两个线程AB都在执行getInstance方法,当A执行第一个if的时候被B抢去了,结果B执行第一个if的时候,又被A抢回去了,这是A会创建实例,B也会创建一个实例,这样就违背了单例模式的要求。所以加上了第二个判空。

但是这样会有一个问题,singleton = new Singleton(); 这句会被JVM差分成多条汇编指令,大致执行了三件事:

  1. 给Singleton实例分配内存
  2. 调用Singleton()的构造函数,初始化成员字段
  3. 将sInstance对象指向分配的内存空间

但是由于JVM允许处理器乱序执行,所以第2步和第3步的执行顺序可能会被打乱,这时如果在执行完第3步而第2步还没有执行的情况下,被切换到了另一个线程上,由于此时第3步已经执行,所以SInstance已经非空了,这时直接取走了sInstance就会出错。

这个问题可以通过Volatile关键字实现,这个关键字能保证可见性和有序性,所以此时如果用Volatile修饰Singleton的话,就能保证按照123的顺序执行。就不会出现问题。

静态内部类单例模式

就是将构造方法放入了类的一个静态内部类中,然后如果要获取这个对象的话,就调用getInstance方法,而getInstance方法就去调用静态内部类的静态变量就可以了。

public class Singleton {  
    private static class SingletonHolder {  
			  private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
		    return SingletonHolder.INSTANCE;  
    }  
}

这样的话,第一次加载Singleton类的时候并不会初始化,而是只有当第一次调用getInstance方法,才能让虚拟机加载静态内部类,也就让静态内部类中的单例类的对象能被初始化。这样不仅保证了线程安全,也保证了单例对象的唯一,同时也延迟了单利的实例化。

5.枚举类型

枚举就是个很神奇的东西。枚举的写法也很神奇,就定义一个枚举类然后写入变量就可以了。这样的简洁,避免多次实例化,最重要的是它支持自动序列化。

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

总结

单例模式分析

使用这个模式时说明系统只需要一个实例对象,如系统要求提供一个唯一的序列号生成器,或者需要考虑资源消耗太大而只允许创建一个对象。

缺点:

  • 单例类的扩展有很大的困难
  • 单例类的职责过重,在一定程度上违背了“单一职责原则”。
  • 如果实例化的对象长时间不被利用,系统会认为它是垃圾,会自动销毁并回收资源,下次利用时又将重新实例化,这将导致对象状态的丢失
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值