GoF23 之 单例模式 创建型模式

单例模式

定义
保证全局绝对只有一个实例对象
单例类必须自己创建自己的唯一实例
单例必须给所有其他对象提供这一实例

单例的实现方式

饿汉模式

类加载时就实例化对象

/*
    饿汉单例模式
    特征 类加载时就实例化对象
    优点 线程安全 还没线程时就实例化了 所以线程绝对安全的
    缺点 可能会带来内存浪费占茅坑不拉屎的想象  适用于实例数量少的场景
*/
public class HungrySingleton {
    // 类加载顺序 先静态后动态 先属性后方法 先上后下
    
    // 类加载时就会被实例化
	private static HungrySingleton instance = new HungrySingleton();
	
    // 构造函数必须私有 这样才不会被外部对象实例化
    private HungrySingleton(){}
    
    // 获取唯一可用的对象 (获取实例有则返回无则创建) static让全局可访问
    public static HungrySingleton getInstance() {
        return instance;
    }
}

懒汉模式

被外部类调用时才初始化实例
/**
 * 懒汉模式
 * 特征 被外部类调用时才初始化实例
 * 优点 不浪费内存 不提前初始化 用时再初始化
 * 缺点 多线程下不安全 
 *
 */
public class LazySingleton {
    
    private static LazySingleton instance = null;

    LazySingleton(){ }
    
    public static LazySingleton getInstance() {
        if(null==instance){
            instance =  new LazySingleton();
        }
        return instance;
    }
}

懒汉模式升级 双重检查加锁 double-checked locking

package com.liao.edu.lazy;
/**
 * 懒汉模式+双重检查加锁
 * 特征 被外部类调用时才初始化实例
 * 优点 调用方法有条件触发锁 锁的粒度更细 线程安全的
 * 缺点 还是会触发锁,会被反序列化破坏
 */
public class LazyDoubleCheckSingleton {
    //volatile  防止指令重拍
    //因为 instance =  new DoubleCheckLazySingleton(); 实例化过程并不是原子性的
    //所以加volatile防止指令重排(123顺序可能错乱 导致其他线程调用时instance!=null )
    private volatile static LazyDoubleCheckSingleton instance = null;

    //私有化保证不被外部初始化
    private  LazyDoubleCheckSingleton(){}

    public static  LazyDoubleCheckSingleton getInstance() {
        //有条件加锁避免调用就上锁 即减少同步锁的竞争
        if(null==instance){
            synchronized (LazyDoubleCheckSingleton.class){
                //保证单例
                if(null==instance){
                    instance =  new LazyDoubleCheckSingleton();//不是原子操作
                    //1.创建内存空间
                    //2.在内存空间内创建对象
                    //3.将内存空间复制给变量instance
                }
            }
        }
        return instance;
    }
}

懒汉模式升级之 静态内部类

package com.liao.edu.lazy;
/**
 * 懒汉模式特征 被外部类调用时才初始化实例
 * 懒汉模式+静态内部类   利用类的加载机制-兼顾了饿汉式单例的空间浪费和懒汉式单例synchronized的性能问题 完美屏蔽了2个缺点
 * 优点 利用java本身语法特点,性能高 内部类一定是在方法调用之前初始化,巧妙地避免了线程安全问题。 不能被反射破坏
 * 缺点 不优雅
 */
public class LazyStaticInnerClassSingleton {

    //默认使用的时候 会先初始化内部类
    //如果没有使用的话 内部类是不加载的
    private LazyStaticInnerClassSingleton(){
        if(InnerClass.lazy!=null){
            throw new RuntimeException("不允许创建多个实例");
        }
    }

    // static 为了使单例的空间共享
    // final 保证方法不回被重写,重载
    public static final LazyStaticInnerClassSingleton getInstance() {
        //在返回结果之前,一定会先加载内部类
        return InnerClass.lazy;
    }
    //默认不加载
    private static final class InnerClass  {
       private static LazyStaticInnerClassSingleton lazy = new LazyStaticInnerClassSingleton();
     }
}

测试静态内部类单例

package com.liao.edu;

import com.liao.edu.lazy.LazyStaticInnerClassSingleton;
import java.lang.reflect.Constructor;

public class StaticInnerLazySingletonTest {
    public static void main(String[] args) {
        try {
            Class<?> clazz = LazyStaticInnerClassSingleton.class;
            //通过反射拿到私有构造方法
            Constructor c = clazz.getDeclaredConstructor(null);
            //强制访问
            c.setAccessible(true);
            //暴力初始化
            Object obj = c.newInstance();
            Object obj2 =  c.newInstance();
            System.out.println(obj==obj2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

结果

false

Process finished with exit code 0

登记式/注册式单例之枚举单例

package com.liao.edu.register;

/**
 * 枚举单例
 *   枚举天生不能被反射破坏 Cannot reflectively create enum objects
 *   枚举天生线程安全 private volatile transient Map<String, T> enumConstantDirectory = null;
 *   优点  写法优雅简单 防止反射 线程安全
 *   缺点  底层是饿汉式的实现 内存浪费 不能大量使用
 */
public enum EnumSingleton {
    INSTANCE;
    private  Object obj;

    public Object getObj() {
        return obj;
    }

    public void setObj(Object obj) {
        this.obj = obj;
    }

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

测试

public class EnumSingletonTest {
    public static void main(String[] args) {
        EnumSingleton n1 = EnumSingleton.getInstance();
        n1.setObj("abc");
        EnumSingleton n2 = EnumSingleton.getInstance();
        n2.setObj("abc");
        System.out.println(n1.getObj());
        System.out.println(n2.getObj());
        System.out.println(n1.getObj()==n2.getObj());
        Class clazz = EnumSingleton.class;
        try {
            Constructor c = clazz.getDeclaredConstructor(String.class,int.class);
            c.setAccessible(true);
            c.newInstance("liao",666);
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

返回结果

abc
abc
true
java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:416)
	at com.liao.edu.EnumSingletonTest.main(EnumSingletonTest.java:22)

Process finished with exit code 0

注册式单例之容器单例

package com.liao.edu.register;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 容器注册单例
 * 适用于单例非常多的情况,便于管理
 */
public class ContainerSingleton {

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

    //无参私有的构造函数
    private ContainerSingleton(){}

    public static Object getInstance(String className) {
        if(!map.containsKey(className)){
            synchronized (ContainerSingleton.class){
                if(!map.containsKey(className)){
                    try{
                        map.put(className,Class.forName(className).newInstance());
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return  map.get(className);
    }

}

测试

package com.liao.edu;

import com.liao.edu.register.ContainerSingleton;

public class ContainerSingletonTest {
    public static void main(String[] args) {
        EnumSingletonTest instance = (EnumSingletonTest) ContainerSingleton.getInstance("com.liao.edu.EnumSingletonTest");
        EnumSingletonTest instance2 = (EnumSingletonTest) ContainerSingleton.getInstance("com.liao.edu.EnumSingletonTest");
        System.out.println(instance);
        System.out.println(instance2);
        System.out.println(instance==instance2);
    }
}

返回结果

com.liao.edu.EnumSingletonTest@135fbaa4
com.liao.edu.EnumSingletonTest@135fbaa4
true

Process finished with exit code 0
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值