设计模式--单例模式

单例模式

单例模式是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式是创建型模式。

应用场景

Spring框架应用中的ApplicationContext、数据库连接池、JDK中Runtime类等。

public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    public static Runtime getRuntime() {
        return currentRuntime;
    }

    private Runtime() {
    }
}

饿汉式

/**
 * 优点:线程绝对安全。执行效率比较高
 * 缺点:所有对象加载的时候就要实例化,如果有大批量单例对象创建,占用大量内存,浪费内存资源
 */
public class HungrySingleton {

    private static final HungrySingleton singleton = new HungrySingleton();

    private HungrySingleton() {
    }

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

懒汉式

/**
 * 优点:提高内存使用
 * 缺点:使用锁,降低执行速度
 */
public class LazySingleton {

    private LazySingleton() {
    }

    private static volatile LazySingleton singleton;

    public static LazySingleton getInstance() {
        //检查是否要阻塞
        if (singleton == null) {
            synchronized (LazySingleton.class) {
                //判断是创建新的对象
                if (singleton == null) {
                    singleton = new LazySingleton();
                    //设置lazy指向刚分配的内存地址
                    //指令重排序的问题
                }
            }
        }
        return singleton;
    }
}

静态内部类单例

package com.example.springtools.util.singleton;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

/**
 * 1.使用静态内部类创建 兼顾饿汉式单例模式的内存浪费问题和synchronized的性能问题
 * 内部类在方法调用之前初始化,避免产生线程安全问题
 * 2.构造方法加入对象判断条件 防止使用反射调用构造方法创建多个对象,破坏单例
 * 3.使用readResolve方法,防止反序列化的问题
 * 通过查看源码得知当对象调用readObject读取对象产生对象时,如果发现对象内部有readResolve,则返回readResolve方法的返回值,
 * 如果不存在,则创建新的对象
 */
public class LazyInnerClassSingleton {

    private LazyInnerClassSingleton() {
        if (LazyHolder.LAZY != null) {
            throw new RuntimeException("不允许创建多个实例");
        }
    }

    public static LazyInnerClassSingleton getInstance() {
        return LazyHolder.LAZY;
    }

    private static class LazyHolder {
        private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
    }

//    序列化
//    FileInputStream fis = new FileInputStream("xx");
//    ObjectInputStream ois = new ObjectInputStream(fis);
//    LazyInnerClassSingleton o = (LazyInnerClassSingleton)ois.readObject();
//    ois.close();

    public LazyInnerClassSingleton readResolve() {
        return LazyHolder.LAZY;
    }
}

注册式单例模式(登记式单例模式)

枚举式单例模式

/**
 * 最优雅的方式
 * 通过查看源码发现枚举采用的是饿汉式单例模式的创建方法,当然也存在内存资源消耗的问题,不适合大量单例对象的创建
 * 并发安全、不会被反射和反序列化破坏
 */
public enum EnumSingleton {

    INSTANCE;

    private Object data;

    private Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static EnumSingleton getInstance() {
        return INSTANCE;
    }
}

容器式单例模式

/**
 * 类似懒汉式,线程不安全
 */
public class ContainerSingleton {

    private ContainerSingleton() {
    }

    private static Map<String, Object> ioc = new ConcurrentHashMap<>();

    public static Object getBean(String className) {
        synchronized (ioc) {
            if (ioc.containsKey(className)) {
                Object obj = null;
                try {
                    obj = Class.forName(className).newInstance();
                    ioc.put(className, obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return obj;
            } else {
                return ioc.get(className);
            }
        }
    }
}

线程单例实现ThreadLocal

/**
 * ThreadLocal不能保证全局唯一,但是可以保证单个线程唯一,天生线程安全
 * 每个对象都会放到ThreadLocalMap中,为每个线程提供一个对象,空间换实现实现线程隔离。
 */
public class ThreadLocalSingleton {

    private static final ThreadLocal<ThreadLocalSingleton> threadLocalInstance =
            new ThreadLocal<ThreadLocalSingleton>() {
                @Override
                protected ThreadLocalSingleton initialValue() {
                    return new ThreadLocalSingleton();
                }
            };

    private ThreadLocalSingleton() {
    }

    public static ThreadLocalSingleton getInstance() {
        return threadLocalInstance.get();
    }

}

总结:

​ 单例模式可以保证内存里只有一个实例,减少内存的开销,还可以避免对资源的多重占用。

posted @ 2021-04-20 23:58  snail灬  阅读(61)  评论(0编辑  收藏  举报