单例设计模式
所谓的单例模式就是,采取一定的方法保证某个类在整个软件只有一个实例,并且该类提供一个获取其唯一实例的方法。
单例模式的常见实现方式有8种,分别为饿汉式(两种),懒汉式(五种),枚举式。
一、饿汉式
1、静态常量实现
public class SingletonHungry1 {
private static SingletonHungry1 mInstance = new SingletonHungry1();
private SingletonHungry1() {
}
public static SingletonHungry1 getInstance() {
return mInstance;
}
}
优点:写法比较简单,在类装载的时候就完成了实例化,避免了线程同步问题。
缺点:在类装载的时候就完成了实例化,没有到达懒加载的效果,如果从始至终未使用过这个实例,则会造成内存浪费。
2、静态代码块实现
public class SingletonHungry2 {
private static SingletonHungry2 mInstance;
static {
mInstance = new SingletonHungry2();
}
private SingletonHungry2() {
}
public static SingletonHungry2 getInstance() {
return mInstance;
}
}
优缺点与静态常量实现相同。
二、懒汉式
1、普通懒汉式实现(线程不安全)
public class SingletonLazy1 {
private static SingletonLazy1 mInstance;
private SingletonLazy1() {
}
public static SingletonLazy1 getmInstance() {
if (mInstance == null) {
mInstance = new SingletonLazy1();
}
return mInstance;
}
}
优点:达到了懒加载的效果,但是只能在单线程下使用。
缺点:如果在多线程下,一个线程进入了if判断语句块,但还未来得及往下执行,同时另一个线程也通过了这个判断语句,这时会产生多个实例。
2、同步方法实现(线程安全)
public class SingletonLazy2 {
private static SingletonLazy2 mInstance;
private SingletonLazy2() {
}
public static synchronized SingletonLazy2 getmInstance() {
if (mInstance == null) {
mInstance = new SingletonLazy2();
}
return mInstance;
}
}
优点:达到了懒加载的效果,并且保障了线程安全。
缺点:执行效率低,每次获取实例的时候都要对方法进行同步,但是我们只需要在实例初始化的时候同步就可以了。
3、同步代码块实现
public class SingletonLazy3 {
private static SingletonLazy3 mInstance;
private SingletonLazy3() {
}
public static SingletonLazy3 getInstance() {
if (mInstance == null) {
synchronized (SingletonLazy3.class) {
mInstance = new SingletonLazy3();
}
}
return mInstance;
}
}
优点:无
缺点:本意是对同步方法实现方式进行优化,结果适得其反,不仅没有提升效率,反而使得线程不安全,因为只是对初始化的代码进行了同步,但是其他线程依然可以进入if判断,这种写法不能使用!
4、双重检查实现
public class SingletonLazy4 {
private static volatile SingletonLazy4 mInstance;
private SingletonLazy4() {
}
public static SingletonLazy4 getmInstance() {
if (mInstance == null) {
synchronized (SingletonLazy4.class) {
if (mInstance == null) {
mInstance = new SingletonLazy4();
}
}
}
return mInstance;
}
}
优点:实现了懒加载的效果,并且保障了是线程安全的,推荐使用。
不要忘记使用volatile关键字,volatile可以保证对象的原子性,当某个线程改变单例对象时可以保证单例在其他线程实时同步更新。如果不加volatile关键字可能导致线程获取单例为null。
5、静态内部类实现
public class SingletonLazy5 {
private SingletonLazy5() {
}
static class SingleInstance {
private static SingletonLazy5 INSTANCE = new SingletonLazy5();
}
public static SingletonLazy5 getInstance() {
return SingleInstance.INSTANCE;
}
}
优点:保证了线程安全,利用静态内部类特点实现了懒加载,效率高。推荐使用。
1)这种方式采取了类装载机制来保证初始化实例时只有一个线程。
2)静态内部类SingleInstance在SingleLazy5装载时并不会立即实例化,而是在调用了getInstance()方法时才会进行装载,从而完成单例类的实例化。
3)类的静态属性只会在第一次加载类的时候初始化,JVM帮我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。
三、枚举式
1、枚举式
public enum SingletonEnum {
//单例本身
INSTANCE;
public void doSome() {
System.out.println("~~~");
}
}
优点:借助jdk1.5添加的枚举实现单例模式,不仅能避免多线程同步问题,还能防止反序列化重新创建新的对象,Effective Java作者提倡的方式,推荐使用