码迷,mamicode.com
首页 > 其他好文 > 详细

单例模式

时间:2017-12-05 15:58:19      阅读:233      评论:0      收藏:0      [点我收藏+]

标签:冲突   resource   就是   部分   语句   zab   指令   object   系统   

1.单例模式是什么

单例模式,是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通
单例模式可以保证系统中,应用该模式的类一个类只有一个实例。即一个类只有一个对象实例

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

2.单例模式解决了什么问题

1.节省资源

节省内存资源,减少无谓GC的消耗.

2.防止冲突

如果一个项目中有多个实例,会造成系统冲突。保证同一时间状态唯一。

3.单例模式用法

1.恶汉单例

恶汉模式:加载时就初始化对象。

1.静态代码块初始化

public class HungrySingleton {
     private static  HungrySingleton hungrySingleton;

     static {
         hungrySingleton = new HungrySingleton();
     }

     private HungrySingleton (){}

     public static HungrySingleton getInstance() {  
     return hungrySingleton;  
     }
}

2.常量初始化

public class HungrySingleton {
     private static final HungrySingleton hungrySingleton = new HungrySingleton();

     private HungrySingleton (){}

     public static HungrySingleton getInstance() {  
     return hungrySingleton;  
     }
}

??此方法由于成员变量修饰为final所以优于静态代码块初始化。但是这两种单例模式有共同的缺点。在类加载时完成初始化将占用一定内存空间,占用的内存空间不会被GC回收。如果项目中一次都没有使用该对象。那么该对象会造成内存浪费。那么我们就需要一种‘懒加载’来实现加载。

2.懒汉单例

懒汉模式:调用时才初始化对象。

1.标准懒汉单例

public class StandardLazySingleton {
    private static StandardLazySingleton standardLazySingleton;

    /**
     * 私有构造方法
     */
    private StandardLazySingleton() {
    }
    public static StandardLazySingleton getInstance() {
        if (standardLazySingleton == null) {
            standardLazySingleton = new StandardLazySingleton();
        }
        return standardLazySingleton;
    }
}

??此种方式的单例模式拥有静态成员变量,可能会出现并发问题。下面是几种变种解决方案。

2.并发环境下懒汉单例

??直接在获取实例方法上加锁

public class LazySingleton {

    private static LazySingleton lazySingleton;

    private LazySingleton() {
    }
    public static synchronized LazySingleton getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }

}

??此方式解决了‘懒加载’的问题,也解决了同步的问题。但是方法上全部加同步造成的效率很低。大多数情况下方法是不需要同步的。

缩小锁定范围.

public class LazySingleton {

    private static LazySingleton lazySingleton;

    private LazySingleton() {
    }
    public static LazySingleton getInstance() {
        if (lazySingleton == null) {
            synchronized(LazySingleton.class) {
                lazySingleton = new LazySingleton();
            }
        }
        return lazySingleton;
    }

}

??此方式缩小了锁定范围,但是同步会出现问题。假如一个线程进入了if (lazySingleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。我们可以在内部再次添加一个if (lazySingleton == null) 的判断。


public class LazySingleton {

    private static volatile LazySingleton lazySingleton;

    private LazySingleton() {
    }
    public static LazySingleton getInstance() {
        if (lazySingleton == null) {
            synchronized(LazySingleton.class) {
                if (lazySingleton == null) {
                    lazySingleton = new LazySingleton();
                }
            }
        }
        return lazySingleton;
    }
}

??以上方式为懒汉模式中推荐的单例使用方式。解决了线程同步的问题,提高了效率。这种方式有个专业的名词叫双重检查(Double-Check)

volatile 关键词的作用:对变量的单次读/写操作可以保证原子性。

??首先我们需要了解jvm实例化一个对象需要哪些过程。
1.分配内存空间。2.初始化对象。3.将内存空间的地址赋值给对应的引用。 由于jvm会对指令进行重新排序2和3的顺序可能打乱。当3在2之前时,在多线程环境下可能会暴露出一个没有对象的引用,从而导致不可预料的后果。为了防止这个问题,我们需要将变量设置为volatile类型的变量。保证其指令的原子性。需要特别注意的是volatile关键字在java1.5之前并不能产生这个功能。

3.静态内部类单例

public class StaticInnerClassSingleton {

    private static class SingletonHolder {
        private static final StaticInnerClassSingleton INSTANCE = new StaticInnerClassSingleton();
    }

    private StaticInnerClassSingleton() {
    }

    public static final StaticInnerClassSingleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

??一个类的静态属性只会在第一加载类时初始化。这是jvm保证的。所以无需担心并发问题。初始化进行一半的时候,别的线程是无法使用的,因为JVM会帮我们强行同步这个过程。静态变量智慧初始化一次,所以也能保证单例。静态内部类方式在StaticInnerClassSingleton类被装载时并不会立即实例化,而是在需要实例化时,调用getInstance方法,才会装载SingletonHolder类,从而完成StaticInnerClassSingleton的实例化。实现了‘懒加载’。但是如果我们访问了其他静态属性这个也会被实例化。

4.容器单例

??大名鼎鼎的spring就是将每一个实现类作为一个bean装入容器中从而实现了单例。下面我们山寨一个简易的容器方式的单例。此种方式能解决单例中的大部分问题,但是不能解决懒加载的问题。然而这种方式极大的降低了代码之间的耦合。

public class ContainerSingleton {

    private static Map<String, Object> objMap = new HashMap<String, Object>();

    private ContainerSingleton() {
    }

    public static void registerService(String key, Object instance) {
        if (!objMap.containsKey(key)) {
            objMap.put(key, instance);
        }
    }
    public static Object getService(String key) {
        return objMap.get(key);
    }
}

需要在初始化项目的过程中将单例放入容器。

5.枚举单例

** 枚举的成员变量就是枚举类的实例。 **

??

public enum SomeThing {
    INSTANCE;
    private Resource instance;
    SomeThing() {
        instance = new Resource();
    }
    public Resource getInstance() {
        return instance;
    }
}

??需要用SomeThing.INSTANCE.getInstance()。首先,在枚举中我们明确了构造方法限制为私有,在我们访问枚举实例时会执行构造方法,同时每个枚举实例都是static final类型的,也就表明只能被实例化一次。在调用构造方法时,我们的单例被实例化。
也就是说,因为enum中的实例被保证只会被实例化一次,所以我们的INSTANCE也被保证实例化一次。
可以看到,枚举实现单例还是比较简单的,除此之外我们再来看一下Enum这个类的声明:

public abstract class Enum<E extends Enum<E>>
        implements Comparable<E>, Serializable

??可以看到,枚举也提供了序列化机制。保证序列化和反序列化是一个对象。
可以这么说,单元素枚举是目前实现单例模式的最佳方法。

4.单例模式的问题

1.不同类加载产生多个实例

(巨坑待填)

2.序列化反序列化产生多个实例

(巨坑待填)

5.单例模式总结

??单例模式是设计模式中相对简单的设计模式,简单的设计模式也有许多复杂的逻辑.在不同的场景中有不同的用法和坑。面对技术要保持谦虚的心态,戒骄戒躁扎实基础。

引用

http://blog.csdn.net/yy254117440/article/details/52305175
https://www.cnblogs.com/zhaoyan001/p/6365064.html
https://www.cnblogs.com/zuoxiaolong/p/pattern2.html

单例模式

标签:冲突   resource   就是   部分   语句   zab   指令   object   系统   

原文地址:http://www.cnblogs.com/yanlong300/p/7987301.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!