Java设计模式——单例模式

Java设计模式——单例模式

我一直觉得,设计模式的思想都是源于生活的。单例在生活之中也是经常用到的,比如国家领导人、某某公司创始人......类似这种独一无二的。单例模式也属于创建型设计模式,确保在任何情况下单例类最多只能有一个实例对象,并且提供全局访问点。单例模式可以保证内存里只有一个实例,减少了内存开销;可以避免对资源的多重占用。反正就是在内存世界里,单例模式的类的实例是独一无二的。当然也有线程单例,就是同一个线程只有一个单例类实例。

饿汉式单例

类加载的时候就立马初始化生成类的实例对象。不管来没来,先吃饱再说。

步骤:

提供一个单例类的私有的最终的静态全局单例类属性变量。

私有化单例类的构造方法。

初始化对象(可以在第一步或者第二步完成)。

提供全局访问点,用于返回对象实现。

代码:

/** * @description: 饿汉式单例 * @author: lmc * @create: 2019-04-02 20:39 **/ public class HungrySingletonOne implements Serializable { /** * 饿汉式单例,在类初始化的时候就进行对象的创建,不存在不同步问题 */ //第一步提供一个私有的最终的静态全局属性变量,用于返回对象。 private static final HungrySingletonOne hungrySingletonOne=new HungrySingletonOne(); //第二步 私有化构造方法 private HungrySingletonOne(){ if(null != hungrySingletonOne){ throw new RuntimeException("单例类,不允许被反射实例化"); } } //提供全局访问点 public static HungrySingletonOne getInstance(){ return hungrySingletonOne; } /** * @description: 重写readResolve方法,防止序列化破坏单例 * @return java.lang.Object * @date 2019/5/25 22:12 * @author lmc */ private Object readResolve(){ return hungrySingletonOne; } } /** * @description: 饿汉式单例 * @author: lmc * @create: 2019-04-02 20:39 **/ public class HungrySingletonTwo { /** * 饿汉式单例,在类初始化的时候就进行对象的创建 */ //第一步提供一个私有的最终的静态全局属性变量,用于返回对象。 private static final HungrySingletonTwo hungrySingletonTwo; static { hungrySingletonTwo=new HungrySingletonTwo(); } //第二步 私有化构造方法 private HungrySingletonTwo(){ if(null != hungrySingletonTwo){ throw new RuntimeException("单例类,不允许被反射实例化"); } } //提供全局访问点 public static HungrySingletonTwo getInstance(){ return hungrySingletonTwo; } /** * @description: 重写readResolve方法,防止序列化破坏单例 * @return java.lang.Object * @date 2019/5/25 22:12 * @author lmc */ private Object readResolve(){ return hungrySingletonTwo; } }

饿汉式单例利弊

利:没有加锁,执行效率高,比懒汉式体验好。饿汉式,在单例类加载的时候就已经初始化好了实例对象,不存在线程安全问题,因此没有共享的说法。

弊:如果单例类不经常使用,占用了内存。

Spring中 IOC容器ApplicationContext本身就是典型的饿汉式单例

饿汉式单例不存在线程安全问题,这里就不做测试结果展示了,上面的代码都是可以直接运行测试的。

懒汉式单例之双重检查锁单例

懒汉式单例类在类加载的时候不会初始化类生成单例类的实例,而是在调用单例类获取实例的方法的时候才会去初始化实例对象,并返回一个单例对象。

因为懒汉式单例,是在单例类的获取实例方法被调用的时候才会去初始化对象,所以存在高并发,线程安全问题。

synchronized用来保证线程安全问题(原理在这里不细说了)

根据业务需求,synchronized关键字能不写在方法上就不要写在方法上,写在方法里面。这样可以避免整个类都被锁住,写在方法里面,其他线程还是能够运行这个方法被锁之前的代码的。性能稍微提供提高一点点。所以我们有了双重检查锁单例。

/** * @description: 简单的懒汉式单例 * @author: lmc * @create: 2019-04-03 08:59 **/ public class LazySimpleSingleton implements Serializable { //第一步 构造方法私有化,并且设置异常防止反射破坏单例 private LazySimpleSingleton(){ if(null != lazySimpleSingleton){ throw new RuntimeException("单例类,不允许被反射实例化"); } } //第二步 定义对象属性 private static volatile LazySimpleSingleton lazySimpleSingleton=null; //第三步 声明全局访问点 /** * @description: 双重检查锁单例 * @return com.lmc.gp12380.pattern.singleton.lazy.LazySimpleSingleton * @date 2019/5/25 21:21 * @author lmc */ public static LazySimpleSingleton getInstance(){ if(null == lazySimpleSingleton){//第一次检查 synchronized (LazySimpleSingleton.class){//加锁 保证线程安全性 if(null == lazySimpleSingleton){//第二次检查 lazySimpleSingleton=new LazySimpleSingleton(); } } } return lazySimpleSingleton; } /** * @description: 重写readResolve方法,防止序列化破坏单例 * @return java.lang.Object * @date 2019/5/25 22:12 * @author lmc */ private Object readResolve(){ return lazySimpleSingleton; } }

双重检查锁单例,可以用 ideadebug线程模式调试测试。

volatile关键字的作用在这里不细说,就是用来保证绝对的线程安全。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppzzd.html