单例模式介绍
单例模式可以说是应用最广泛的模式之一,也是很多程序猿学会的第一个设计模式,在使用单例模式中,必须保证单例模式的类只有一个实例存在,这就是单例模式
使用场景
因为很多类的对象生成需要消耗大量的资源,例如创建一个对象需要大量时间初始化、访问IO、数据库或者对象本身会消耗很多内存空间以及对象本身存在对象池(线程池等),或者某种类型的对象应该有且只有一个的时候,这时候就可以考虑使用单例模式
单例模式的实现方式
所有方式共同点
单例模式有多种实现方法,如饿汉模式、懒汉模式、Double check Lock(DCL)模式、内部静态类模式、枚举模式等(下面会一一介绍),但是所有模式都一个共同点,那就是对需要实现单例的类的构造方法私有化,简单来说就不能在其他类中使用new关键字进行实例化
饿汉模式
public class Singlecase{
private static final Singlecase single= new Singlecase();
private Singlecase(){}
public static Singlecase getInstance(){
return single;
}
}
优点
线程安全,没有加锁,执行效率会提高
缺点
类加载时就初始化,浪费内存
懒汉模式
public class Singlecase{
private static Singlecase single;
private Singlecase(){}
public static synchronized Singlecase getInstance(){
if(single == null){
single = new Singlecase();
}
return single;
}
}
优点
使用的时候才会实例化,在某种程度上节约了资源
缺点
每次调用getInstance都会进行同步,造成不必要的同步开销,所以这种模式一般不建议使用
Double check Lock(DCL)模式
public class Singlecase {
private static Singlecase single;
private Singlecase(){}
public static Singlecase getInstance(){
if(single == null){
synchronized (Singlecase.class) {
if(single == null){
single = new Singlecase();
}
}
}
return single;
}
}
优点
资源利用率高,效率高
缺点
第一次加载稍慢,而且由于Java内存模型的原因,有可能会失败,同时在高并发的环境也有一定缺陷,虽然几率很小
静态内部类模式
public class Singlecase {
private Singlecase(){}
public static Singlecase getInstance(){
return SinglecaseHolder.single;
}
private static class SinglecaseHolder{
private static Singlecase single = new Singlecase();
}
}
因为当加载Singlecase 类的时候,并不会初始化single ,只有第一次调用Singlecase.getInstance方法时才会加载SinglecaseHolder类导致single被初始化,因此这种方法不仅能确定线程安全,也能保证对象唯一性,同时也延迟了单例的实例化,所以一般都推荐使用这种单例模式
枚举模式
public enum Singlecase {
SINGLE;
public void doSometh(){
System.out.println("doSometh");
}
}
Java中,枚举不仅能够有字段,还能够有自己的方法,而且枚举实例的创建默认是线程安全的,并且在任何情况下都是单例,之前的几种模式虽然外部不能实现,但是如果通过反序列化还是可以创建多个实例的,当然,如果重写了反序列化的readResolve()方法,也可以避免,不过相信一般也不会有人这么较劲非得要用这种方法来创建多个实例,枚举模式最大的优点就是写法简单粗暴
总结
单例模式优点
1.在内存中只有一个实例,减少了内存开支,特别是对象需要频繁创建和销毁对象时,而且创建或销毁时性能优化点不大,这时候单例模式的优点就特别明显了
2.当一个对象产生需要比较多的资源时,则可以通过单例模式,来解决多个对象资源占用大的问题
3.可以避免对资源的多重占用
4.单例模式可以在系统设置全局的访问点,优化和共享资源访问
缺点
1.单例模式没有接口,扩展困难,一般都是通过直接修改源码扩展
2.避免单例模式中持有生命周期短暂的对象引用,容易造成内存泄露,如果持有此类对象,最好在使用后及时释放
最后一个小广告
欢迎大家吐槽交流(QQ群:123965382)