这个模式就是词面意思,就是一个类就只有一个实例,这样可以实现资源的复用。
一,写法有以下几种:
1,懒汉式:为啥叫懒汉呢,因为它可以实现延时加载,当类初建时,只是声明了对象,只有用到时还会初始化,比较懒。缺点是不能保证单例,当线程比较多时肯定不行,如果你能确保调用这个类时是单线程并且想保证时效的话,可以酱紫。
public class LazySingleton { private static LazySingleton instance; private LazySingleton() { } public static LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
2,饿汉式:在类加载时就初始化了这个静态对象,比较饿。缺点是无论使用与否都会初始化,优点是利用了类的加载机制实现了线程安全,JVM通过锁保证类的加载只会有一次。
public class HungrySingleton { private static final HungrySingleton mInstance = new HungrySingleton (); public static HungrySingleton getInstance() { return mInstance; } }
3,DCL模式(双重检查锁定模式):
public class DclSingleton { //加volatile防止指令重排序,这样当任何一个线程的值改变了,都会同步到主线程中。操作时都会先从主内存更新这个值的变量。 private volatile static DclSingleton instance; public static DclSingleton getInstance() { if (instance == null) { //先检查一次,避免加锁 synchronized (DclSingleton.class) { //加锁 if (instance == null) { //第二次检查,防止在第一次检查和加锁间隙被其他线程初始化了 //初始化,该操作是非原子性操作,主要操作有:1,为新的对象分配堆内存 2,调用构造方法,初始化成员变量 3,将instance这个引用指向新分配的DclSingleton对象的地址。这儿可能会指令重排序,123和132结果都一样 // 多线程时,每个线程的内存都有一个instance,当在该线程初始化对象后,主内存可能没及时更新,或者主内存更新了,但其他线程还是用的线程内存里的对象,线程的内存没及时更新。所以要加volatile instance = new DclSingleton(); } } } return instance; } }
4,静态内部类:利用雷德加载机制实现线程安全,而且也是调用时才会初始化。
public class StaticInnerSingleton { public static StaticInnerSingleton getInstance() { return SingletonHolder.instance; } private static class SingletonHolder { private static final StaticInnerSingleton instance = new StaticInnerSingleton(); } }
二,注意事项
1,因为静态对象的生命周期和应用一样,所以当需要传context时,很有可能会内存泄漏,此时最好是传 getApplicationContext()
2,当引用View时,最好采用弱引用的方式,因为被弱引用关联的对象,无论内存是否充足,都只是生存到下一次垃圾回收时。
private WeakReference<View> view = null; public void setMyView(View view) { this.view = new WeakReference<View>(view); }
3,单例模式一般不可扩展。