面试题:单例模式及其五种实现方式

单例模式其实在我们平常并不少见,比如任务管理器、回收站、项目中读取配置文件的类、Application都是典型的单例模式,网站的计数器、数据库连接池等的设计一般也是采取单例模式。

一.单例模式的特点

  1. 只有一个实例
  2. 自行实例化(必须创建自己的唯一实例)
  3. 向整个系统提供这个实例

通俗一点的说就是,保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

  • 其优点就是因为只生成一个实例,减少了系统性能开销,当一个对象需要较多资源时,如读取配置,则可以通过在应用启动时直接产生一个单例对象,然后永久驻留内存来解决。
  • 单例模式可以在系统设置全局的访问点,优化功效资源访问。

二.单例模式的实现方式

主要方式:

单例模式主要是通过以下思路实现:

  • 构造器私有化(避免外部new构造器)
  • 提供私有的静态属性(存储对象的地址)
  • 提供公共的静态方法(获取属性)

其中在提供私有的静态属性时就new实例,称为饿汉式(饿了就吃), 在提供的公共的静态方法中new实例,成为懒汉式(饿了才开始做)。

1.饿汉式单例:线程安全,调用效率高,不能延时加载
public class Singleton {
    private static final Singleton instance = new Singleton();

    //私有构造器,保证了Singleton的全局唯一性,限制产生多个对象
    private Singleton() {
    }

    //静态工厂方法,对于此方法的调用,都会返回同一个对象引用
    public static Singleton getInstance() {
        return instance;
    }

}

饿汉式:

  • 提供私有的静态属性时就new实例,所以在类初始化时,会立即加载这个属性,提供了一个对象供外部使用,除非系统重启,这个对象不会改变,因此是不能延时加载,线程安全的。
  • 也正因为是天然系统安全的,所以可以省略synchronized同步,因此调用效率高。
2.懒汉式单例:线程不安全,调用效率不高,可以延时加载
  private static Singleton singleton = null;

    private Singleton() {
    }

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

懒汉式:

  • 因为在提供的公共的静态方法中才new实例,所以在多线程环境下会产生多个singleton对象,是线程不安全的,因此不建议。
  • 如果想解决线程安全可以考虑使用synchronized同步锁,即synchronized修饰getSingleton() 方法,也可以使用synchronized修饰代码块。
    如:
private static Singleton singleton = null;     
private Singleton() {
    }    
public static synchronized Singleton getSingleton() {
        if (singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }

注:同步锁虽然解决了多个singleton对象问题,但每次调用getSingleton()方法都要同步,会导致允许效率低下。

其他方式:

1.双重检测锁式

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

双重检测锁式:

  • 这个模式将同步内容下放到if内部,提高了执行的效率,不必每次都进行同步,只有第一次才同步创建了以后就没有必要了。
  • 但是由于JVM底层内部模型原因,偶尔会出问题,不建议使用。

2.静态内部类式(线程安全,调用效率高,可以延时加载)

private static class SingletonClassInstance{
     private static final Singleton instance = new Singleton();    
   }
private Singleton() {
    }    
public static Singleton getSingleton() {
  return SingletonClassInstance.instance ;
    }

静态内部类式:

  • 外部没有static属性,因此不会像饿汉式那样立即加载对象。
  • 只有真正调用了getInstance(),才会加载静态内部类。加载类是是线程安全的。instance 是 private static final ,保证了内存中只有这样的一个实例。
  • 结合了并发高校调用和延迟加载的优势。

3.枚举(线程安全,调用效率高,不能延时加载)

public enum Singleton {
//枚举元素,本身就是单例对象
    INSTANCE;
    //添加自己需要的操作
    public void SingletonOpt(){}
}

枚举式:

  • 从Java1.5发行版本开始,这种方法更加简洁,并且无偿提供了序列化机制,绝对防止多次实例化。
  • 虽然这种这种方法还没有广泛使用,但是单元素的枚举类型已经成为实现Singleton的最佳方法

三.应用

说明:中国历史上的皇帝通常仅有一人。为了保障其唯一性,古人采用玉玺等来进行防伪标识。更简单的方法是限制皇帝的创建。比如,使用单例模式来保证皇帝的唯一性。
Emperor类:

public class Emperor
{
    private static Emperor emperor=null;
    private Emperor(){}
  public static Emperor getEmperor(){
        if (emperor==null){
            emperor=new Emperor();
        }
        return emperor;
  }
}

注意线程安全问题,可以自己思考以下改动(^U^)ノ~YO


最近才开始了解设计模式的,所以这也只是看了一些文章之后做的整合,自己的一些看法,如果有什么地方不对的,欢迎大家指出。
学习参考:
https://blog.csdn.net/jason0539/article/details/44956775
《effective Java中文版第二版第二章第三条》《Java编程300例》《尚云堂视频讲解》

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值