/** * 饿汉式单例 * 该模式的特点是类一旦加载就创建一个单例,保证在调用 getInstance 方法之前单例已经存在了。 * 饿汉式单例在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以是线程安全的, * 可以直接用于多线程而不会出现线程安全问题。 * <守护线程或初始化资源线程 可以使用> */ public class HungrySingleton { private static final HungrySingleton instance = new HungrySingleton(); // 1. ? private HungrySingleton() {} // 2. ? public static HungrySingleton getInstance() { //3. return instance; } }
/** * 饿汉式的拓展写法 */ public class HungrySingleton2 { private static HungrySingleton2 instance = null; ? static { instance = new HungrySingleton2(); } ? private HungrySingleton2() {} ? public static HungrySingleton2 getInstance() { return instance; } }
public class LazySingleton { //懒汉式单例模式 //比较懒,在类加载时,不创建实例,因此类加载速度快,但首次运行时获取对象的速度慢 ? private static LazySingleton intance = null;//静态私用成员,没有初始化 ? private LazySingleton() { //私有构造函数 } ? //静态,公开访问点 public static LazySingleton getInstance() { if (intance == null) { intance = new LazySingleton(); } return intance; } }
这种写法是在多线程环境中是存在线程安全问题的
接下来才有了对此不断改进的写法:
/** * 懒汉式单例 * 该模式的特点是类加载时没有生成单例,只有当第一次调用 getlnstance 方法时才去创建这个单例 * 如下需要注意三点 * 如果编写的是多线程程序,则不要删除上例代码中的关键字 volatile 和 synchronized,否则将存在线程不安全的问题 * 如果不删除这两个关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。 * <使用次数少可以采用> */ public class LazySingleton { private static volatile LazySingleton instance = null; //1. 保证 instance 在所有线程中同步 ? private LazySingleton() {} // 2. private 避免类在外部被实例化 ? public static synchronized LazySingleton getInstance() { // 3.getInstance 方法前加同步 if (instance == null) { instance = new LazySingleton(); } return instance; } }
/** * 双重锁校验的单例 */ public class DoubleLock implements Serializable { ? public static volatile DoubleLock doubleLock = null;//volatile防止指令重排序,内存可见(缓存中的变化及时刷到主存,并且其他的内存失效,必须从主存获取) ? private DoubleLock() { //构造器必须私有 不然直接new就可以创建 } ? public static DoubleLock getInstance() { //第一次判断,假设会有好多线程,如果doubleLock没有被实例化,那么就会到下一步获取锁,只有一个能获取到, //如果已经实例化,那么直接返回了,减少除了初始化时之外的所有锁获取等待过程 if (doubleLock == null) { synchronized (DoubleLock.class) { //第二次判断是因为假设有两个线程A、B,两个同时通过了第一个if,然后A获取了锁,进入然后判断doubleLock是null,他就实例化了doubleLock,然后他出了锁, //这时候线程B经过等待A释放的锁,B获取锁了,如果没有第二个判断,那么他还是会去new DoubleLock(),再创建一个实例,所以为了防止这种情况,需要第二次判断 if (doubleLock == null) { //下面这句代码其实分为三步: //1.开辟内存分配给这个对象 //2.初始化对象 //3.将内存地址赋给虚拟机栈内存中的doubleLock变量 //注意上面这三步,第2步和第3步的顺序是随机的,这是计算机指令重排序的问题 //假设有两个线程,其中一个线程执行下面这行代码,如果第三步先执行了,就会把没有初始化的内存赋值给doubleLock //然后恰好这时候有另一个线程执行了第一个判断if(doubleLock == null),然后就会发现doubleLock指向了一个内存地址 //这另一个线程就直接返回了这个没有初始化的内存,所以要防止第2步和第3步重排序 // 采用volatile修饰单例引用 doubleLock = new DoubleLock(); } } } return doubleLock; } }
其实上面的这个写法经历了一个 没有写volatile修饰单例引用的中间过程,后来再次优化才变成这个写法
/** * 线程安全的懒汉式单例 * 用静态内部类实现单例模式 * * 1.从外部无法访问静态内部类LazyHolder,只有当调用Singleton.getInstance方法的时候, * 才能得到单例对象INSTANCE。 * * 2.INSTANCE对象初始化的时机并不是在单例类Singleton被加载的时候,而是在调用getInstance方法, * 使得静态内部类LazyHolder被加载的时候。因此这种实现方式是利用classloader * 的加载机制来实现懒加载,并保证构建单例的线程安全。 * * 真正的懒加载方式 */ public class InnerClassSingleton { // 私有内部类,按需加载,用时加载,也就是延迟加载 private static class Holder { // jvm保证在任何线程访问singleton5静态变量之前一定先创建了此实例 private static final InnerClassSingleton singleton5 = new InnerClassSingleton(); } ? private InnerClassSingleton() { } ? public static InnerClassSingleton getSingleton5() { return Holder.singleton5; } }
/** * 线程安全的懒汉式单例 */ public class ThreadLocalSingleton { // ThreadLocal 线程局部变量 private static ThreadLocal<ThreadLocalSingleton> threadLocal = new ThreadLocal<ThreadLocalSingleton>(); private static ThreadLocalSingleton singleton4 = null; ? private ThreadLocalSingleton() {} ? public static ThreadLocalSingleton getSingleton4() { if (threadLocal.get() == null) { // 第一次检查:该线程是否第一次访问 createSingleton4(); } return singleton4; } ? public static void createSingleton4() { synchronized (ThreadLocalSingleton.class) { if (singleton4 == null) { // 第二次检查:该单例是否被创建 singleton4 = new ThreadLocalSingleton(); // 只执行一次 } } threadLocal.set(singleton4); // 将单例放入当前线程的局部变量中 ? } }
/** * 类似Spring里面的方法,将类名注册,下次从里面直接获取 */ public class RegisteredSingleton { // 存储需要进行维护和管理的类的实例 private static Map<String, RegisteredSingleton> map = new HashMap<String, RegisteredSingleton>(); static { // 静态创建实例并添加到Map集合 RegisteredSingleton singleton = new RegisteredSingleton(); map.put(singleton.getClass().getSimpleName(), singleton); } ? // 保护的默认构造子 protected RegisteredSingleton() { } ? // 静态工厂方法,返还此类惟一的实例 public static RegisteredSingleton getInstance(String name) { if (name == null) { name = RegisteredSingleton.class.getName(); System.out.println("name == null" + "--->name=" + name); } if (map.get(name) == null) { try { map.put(name, (RegisteredSingleton) Class.forName(name).newInstance()); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } } return map.get(name); } ? public static void main(String[] args) { RegisteredSingleton singleton = RegisteredSingleton.getInstance("RegisteredSingleton"); System.out.println(singleton); } }
单元素的枚举类型已经成为实现Singleton的最佳方法
-- 出自 《effective java》
public enum EnumSingleton { INSTANCE; public EnumSingleton getInstance(){ return INSTANCE; } }
目前所有实现单例主流方式的汇总就告一段了。
下一篇讲一讲 ,单例模式的安全攻击问题(如何防止 反射 序列化 克隆 类加载器 导致 单例模式结构约束的破坏问题)
?
原文:https://www.cnblogs.com/dukang-bigdataJava/p/13650889.html