设计模式-单例模式
定义
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
类图
类型
饿汉式
线程安全,调用效率高,但是不能延迟加载。
public class HungrySingleton
{
private static final HungrySingleton instance=new HungrySingleton();
private HungrySingleton(){}
public static HungrySingleton getInstance()
{
return instance;
}
}
懒汉式
线程安全,调用效率不高,并且可以延迟加载。
public class LazySingleton
{
private static volatile LazySingleton instance=null; //保证 instance 在所有线程中同步
private LazySingleton(){} //private 避免类在外部被实例化
public static synchronized LazySingleton getInstance()
{
//getInstance 方法前加同步
if(instance==null)
{
instance=new LazySingleton();
}
return instance;
}
}
双重检查锁式
双重检查锁模式解决了单例、性能、线程安全问题。但在多线程情况下,可能会出现空指针问题。问题在于JVM 在实例化对象时会进行优化和指令重排序操作。解决空指针问题只需使用volatile 关键字,volatile 可以保证可见性和有序性。
public class LazyMan3 {
private LazyMan3(){}
private static volatile LazyMan3 instance;
public static LazyMan3 getInstance(){
//第一次判断,如果instance不为null,不需要抢占锁,直接返回对象
if (instance == null){
synchronized (LazyMan3.class){
//第二次判断
if (instance == null){
instance = new LazyMan3();
}
}
}
return instance;
}
}
class LazyMan3Test{
public static void main(String[] args) {
LazyMan3 instance = LazyMan3.getInstance();
LazyMan3 instance1 = LazyMan3.getInstance();
System.out.println(instance == instance1);
}
}
静态内部类式
线程安全,调用效率不高,可以延迟加载;
public class LazyMan4 {
private LazyMan4(){}
//定义一个静态内部类
private static class LazyMan4Holder{
private static final LazyMan4 INSYANCE = new LazyMan4();
}
//对外访问方法
public static LazyMan4 getInstance(){
return LazyMan4Holder.INSYANCE;
}
}
class LazyMan4Test{
public static void main(String[] args) {
LazyMan4 instance = LazyMan4.getInstance();
LazyMan4 instance1 = LazyMan4.getInstance();
System.out.println(instance == instance1);
}
}
静态代码块式
public class HungryChinese2 {
//私有构造方法,为了不让外界创建该类的对象
private HungryChinese2(){}
//声明该类类型的变量
private static HungryChinese2 hungryChinese2;//初始值为null
//静态代码块中赋值
static {
hungryChinese2 = new HungryChinese2();
}
//对外提供的访问方式
public static HungryChinese2 getInstance(){
return hungryChinese2;
}
}
class HungryChinese2Test{
public static void main(String[] args) {
HungryChinese2 instance = HungryChinese2.getInstance();
HungryChinese2 instance1 = HungryChinese2.getInstance();
System.out.println(instance.equals(instance1));
}
}
枚举式
线程安全,调用效率高,不能延迟加载,但是可以天然的防止反射和反序列化漏洞。
public enum LazyMan5 {
INSTANCE;
}
class LazyMan5Test{
public static void main(String[] args) {
LazyMan5 instance = LazyMan5.INSTANCE;
LazyMan5 instance1 = LazyMan5.INSTANCE;
System.out.println(instance == instance1);
}
}
应用场景
-
某个类只要求生成一个对象的时候。
-
当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如Web中的配置对象、数据库的连接池等。
-
当某类需要频繁实例化,而创建的对象又频繁被销毁时,如多线程的线程池、网络连接池等。
单例问题
Serializable
- 问题:如果单例类实现了java.io.Serializable 接口,那么这个类可能会被反序列化,并且反序列化多次使用同一对象时,会得到多个单例类的实例,这样就不是单例了;
- 解决方法:需要添加如下方法
// 反序列化时,如果定义了readResolve() 则直接返回此方法指定的对象,而不需要单独再创建新对象了;
private Object readResovle() throws ObjectStreamException{
// TODO Auto-generated method stub
return instance;
}
在Android 中使用单例模式可能会内存泄漏
- 问题:当调用getInstance 时,如果传入的context是Activity 的context 。只要这个单例没有被释放,那么这个Activity 也不会被释放,一直到进程退出才会释放。
public class CommUtils {
private volatile static CommUtils mCommUtils;
private Context mContext;
public CommUtils(Context context) {
mContext=context;
}
public static CommUtils getInstance(Context context) {
if (mCommUtils == null) {
synchronized (CommUtils.class) {
if (mCommUtils == null) {
mCommUtils = new CommUtils(context);
}
}
}
return mCommUtils;
}
}
- 解决方法:能使用Application的Context就不要使用Activity的Context,Application的生命周期伴随着整个进程的周期。
原文地址:https://blog.csdn.net/weixin_41645817/article/details/145213819
免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!