几种常见的单例模式的写法及优缺点比较
概述
今天再来总结一篇开发中常用的设计模式单例模式,其定义是单例对象的类只能允许一个实例存在,它虽然有多种常见的写法,但是其基本思想不变:
- 构造器私有化,使外部无法无法new 对象
- 提供一个获取对象的方法 即提供一个get方法获取第三部中的属性,但是要求所有对象共用故而用static修饰
- 内部提供一个实例 作为属性 要求私有化 静态化
private static
单例模式几种常见的写法
1:饿汉模式
- 优点:写法比较简单、线程安全,在类加载的时候就完成实例化。
- 缺点:在类加载的时候就完成实例化,没有达到Lazy Loading(懒加载)的效果。如果从始至终从未使用过这个实例,则会造成内存的浪费。
public class SingleInstance {
//饿汉模式线程安全
//step1 :私有化构造器
private SingleInstance() {
}
//step2 :提供私有化静态属性
private static SingleInstance sSingleInstance = new SingleInstance();
//step3 : 通过获取该属性的方法
public static SingleInstance getInstance() {
return sSingleInstance;
}
}
2:懒汉模式
- 优点:这种方式实现了Lazy Loading的效果,单线程提可保证单例
- 缺点:线程不安全,只适用于单线程不适用于多线程
public class SingleInstance {
//懒汉模式
//step1 :私有化构造器
private SingleInstance(){}
//step2 :提供私有化静态属性
private static SingleInstance sInstance;
//step3 : 通过获取该属性的方法
public static SingleInstance getInstance(){
if (sInstance == null) {
sInstance=new SingleInstance();
}
return sInstance;
}
}
3:懒汉模式:同步方法
通过synchronized
修饰该方法,保证线程安全
- 优点:实现了懒加载,并且线程安全
- 缺点:效率低不推荐使用,因为使用了
synchronized
修饰方法,每次使用时都得同步
public class SingleInstance {
//懒汉模式 同步方法
//step1 :私有化构造器
private SingleInstance(){}
//step2 :提供私有化静态属性
private static SingleInstance sInstance;
//step3 : 通过获取该属性的方法 synchronized修饰该方法
public static synchronized SingleInstance getInstance(){
if (sInstance == null) {
sInstance=new SingleInstance();
}
return sInstance;
}
}
4:懒汉模式:同步代码块
通过synchronized
修饰该方法,保证线程安全
- 优点:实现了懒加载,
- 缺点:线程不安全,和方法二一样不适用于多线程,不推荐使用
public class SingleInstance {
//懒汉模式 同步代码快
//step1 :私有化构造器
private SingleInstance(){}
//step2 :提供私有化静态属性
private static SingleInstance sInstance;
//step3 : 通过获取该属性的方法 synchronized修饰该方法
public static SingleInstance getInstance(){
if (sInstance == null) {
synchronized (SingleInstance.class){
sInstance=new SingleInstance();
}
}
return sInstance;
}
}
5:双重锁机制(推荐)
优点:实现了懒加载,线程安全,效率高,第一层判null提高了锁机制的使用效率,第二层判null才是真正意义的单例。
public class SingleInstance {
//双重锁
//step1 :私有化构造器
private SingleInstance(){}
//step2 :提供私有化静态属性
private static SingleInstance sInstance;
//step3 : 通过获取该属性的方法 synchronized修饰该方法
public static SingleInstance getInstance(){
if (sInstance == null) {//第一层:保证的多线程只有第一次调用getInstance 的时候才会加锁初始化
synchronized (SingleInstance.class){
if (sInstance==null) {// 第二层 实现了单例模式
sInstance=new SingleInstance();
}
}
}
return sInstance;
}
6:静态内部类(推荐)
优点:避免了线程不安全,延迟加载,效率高。
public class SingleInstance {
//静态内部类
//step1 :私有化构造器
private SingleInstance(){}
//step2 :提供私有化静态属性
private static SingleInstance sInstance;
//step3 : 通过获取该属性的方法 synchronized修饰该方法
public static SingleInstance getInstance(){
//只有调用此方法的时候才会初始化单例,实现了懒加载,因为类(Single)的加载只有一次,从而保证了单例
return Single.sInstance;
}
private static class Single{
private static SingleInstance sInstance=new SingleInstance();
}
}
这种方式跟饿汉式方式采用的机制类似,但又有不同。两者都是采用了类装载的机制来保证初始化实例时只有一个线程。不同的地方在饿汉式方式是只要Singleton类被装载就会实例化,没有Lazy-Loading的作用,而静态内部类方式在Singleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonInstance类,从而完成Singleton的实例化。
类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
单例模式的优缺点以及使用场景
优点: 系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
优点:
系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能。
缺点:
单例类的职责过重,在一定程度上违背了“单一职责原则”。
适用场景:
- 工具类对象;
- 需要频繁的进行创建和销毁的对象,并且创建对象时耗时过多或耗费资源过多,如:访问I0和数据库等资源或者有很多个地方都用到了这个实例。
- 频繁访问数据库或文件的对象,