单例模式

单例模式

  • 实现方式:把构造方法私有化,不让外部进行访问,在类中创建私有化对象,调用类中的公有方法返回对象或创建对象
  • 意图:节省资源 避免一个对象频繁的创建与销毁

以下总结单例模式的七种实现方式

1.饿汉式
顾名思义:我想去创建(拥有)一个对象------类加载的时候就创建对象
创建对外提供方法返回对象
  • 好处:线程安全(类加载机制),没有加锁=执行效率要高
  • 缺点:在类加载的时候创建对象,若用不上的该对象,会浪费资源(占空间)
    对于反射和反序列化是不安全的
//实现代码
public class Singleton_01 implements Serializable {
    private Singleton_01(){};
    private static volatile Singleton_01 singleton_01 = new Singleton_01();
    public static Singleton_01 getInstance(){
        return singleton_01;
    }
//因为静态的变量在序列化过程中是不会被保存的,所以在反序列化时会重新生成实例
    private Object readResolve(){
        //反序列化内部其实利用的还是反射,目标类存在readResolve时,做的是一个浅拷贝
        /*如果单例对象中定义了readResolve()方法,则对前面生成的对象进行覆盖,来保证单例。
        实际上实例化了两次,只不过第二次实例化的对象没有被返回而已*/
        return singleton_01;
    }

}
	//在测试类中测试
	//true
	@Test
    public void text01() {
        Singleton_01 instance = Singleton_01.getInstance();
        Singleton_01 instance1 = Singleton_01.getInstance();
        System.out.println(instance.equals(instance1));
    }
    //饿汉式 反射不安全
    //false
    @Test
    public void text001() throws Exception {
        Class clazz = Singleton_01.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton_01 instance = Singleton_01.getInstance();
        Singleton_01 instance1 = (Singleton_01) constructor.newInstance();
        System.out.println(instance==instance1);
    }

	//饿汉式 反序列化不安全 添加readResolve()可以使其变得安全
	//测试先执行序列化,然后执行反序列化 结果为 true
    @Test
    public void test0001(){
        Singleton_01 s1 = Singleton_01.getInstance();
        SerializeUtil.serialize(s1);
        /*Singleton_01 s1 = (Singleton_01) SerializeUtil.unserialize();
        Singleton_01 s2 = (Singleton_01) SerializeUtil.unserialize();
        System.out.println(s1==s2);*/
    }
    
2.懒汉式
顾名思义:我想去懒得(拥有)一个对象(lazy loading)------类加载的时候没有创建对象 
创建对外提供方法返回对象
  • 好处:再调用对外提供的方法时,创建对象,不会浪费空间
  • 缺点:线程不安全
public class Singleton_02 {
    private Singleton_02(){}
    private static Singleton_02 singleton_02 = null;
    public static /*synchronized*/ Singleton_02 getInstance(){
        synchronized (Singleton_02.class){
            //同步代码块
        }
        if (singleton_02==null) {
            /*在这里时容易产生线程不安全*/
            /*      eg:
             *           线程1判定为空后sleep了一会,线程2此时进来了
             *
             *      解决方式:可在对外提供的方法上加入synchronized关键字保证线程安全
             *                      ---不过也有缺点,因为加了synchronized肯定会导致效率变低(很小)
             */
            singleton_02 = new Singleton_02();
        }
        return singleton_02;
    }


}
//懒汉式单线程
//true
    @Test
    public void text02() {
        Singleton_02 instance = Singleton_02.getInstance();
        Singleton_02 instance1 = Singleton_02.getInstance();
        System.out.println(instance.equals(instance1));
    }
    
3.懒汉式 双检索——可以看成升级版的懒汉
   小几率指令重排
   需要加上volatile
   singleton_02 = new Singleton_002(); 会执行如下操作:
   1.分配对象内存空间
   2.初始化对象
   3.singleton_02指向 1 中分配的空间

  在某些编译器上,可能出现指令重排:
   1.分配对象内存空间
   2.singleton_02指向 1 中分配的空间(但此时对象没有初始化)
   3.初始化对象
public  class  Singleton_002 {
    private Singleton_002() {
    }

    private  static volatile Singleton_002 singleton_02 = null;

    public static /*synchronized*/ Singleton_002 getInstance() {
        //在第一重发现不为null 不会向下继续执行同步代码块
            //如果不加 则每一次都会进入同步 效率低
        if (singleton_02 == null) {
            synchronized (Singleton_002.class) {
                //同步代码块
                if (singleton_02 == null) {
                    singleton_02 = new Singleton_002();
                }
            }
        }
        return singleton_02;
    }


}

 //懒汉式多线程
    @Test
    public void text002() {
        for (int i = 0; i < 20; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Singleton_02.getInstance());
                }
            }).start();
        }
        try {
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
4.静态内部类
通过类加载的机制创建对象(静态内部类)
创建一个私有内部类,再类中对外部类的创建,并私有化
一个公开的方法,返回内部类所创建的对象
  • 好处:由于类加载器加载时的安全机制,所以内部类所创建的对象是安全的
public class Singleton_03 {
    private Singleton_03(){
        //禁用反射 防止反射攻击
        if (SingletonHolder.SINGLETON_03!=null){
            throw new IllegalStateException();
        }
    };
    public static class SingletonHolder{
        private static final Singleton_03 SINGLETON_03 = new Singleton_03();
    }
    public static final Singleton_03 getInstance(){
        return SingletonHolder.SINGLETON_03;
    }
}
//静态内部类 登记式
//true
    @Test
    public void test03() {
        Singleton_03 instance = Singleton_03.getInstance();
        Singleton_03 instance1 = Singleton_03.getInstance();
        System.out.println(instance.equals(instance1));
    }
 //普通登记式无法保证反射安全
 //抛出异常
    @Test
    public void test003() throws Exception {
        Class clazz = Singleton_03.class;
        Constructor constructor = clazz.getDeclaredConstructor();
        constructor.setAccessible(true);
        Singleton_03 instance1 = (Singleton_03) constructor.newInstance();
        Singleton_03 instance = Singleton_03.getInstance();
        System.out.println(instance == instance1);
    }
5.枚举式
简单高效
线程安全
不是延迟初始化的,立即初始化
自动支持序列化,防止反序列化创建一个新的对象
防止反射攻击
public enum  Singleton_04 {
    INSTANCE{
        @Override
        protected void doSomething() {

        }
    };
    protected abstract void doSomething();
}

//枚举式
    @Test
    public void test04(){
        Singleton_04 instance = Singleton_04.INSTANCE;
        Singleton_04 instance1 = Singleton_04.INSTANCE;
        System.out.println(instance==instance1);
    }
    //对于反射默认就是安全的
    @Test
    public void test004() throws Exception {
        Class<Singleton_04> aClass = Singleton_04.class;
        Constructor<Singleton_04> constructor = aClass.getConstructor();
        Singleton_04 singleton_04 = constructor.newInstance();
        Singleton_04 instance = Singleton_04.INSTANCE;
        System.out.println(singleton_04==instance);
    }
    //枚举 序列化
    @Test
    public void test0004() throws Exception {
        /*Singleton_04 instance = Singleton_04.INSTANCE;
        SerializeUtil.serialize(instance);*/
        Singleton_04 unserialize = (Singleton_04) SerializeUtil.unserialize();
        Singleton_04 unserialize1 = (Singleton_04) SerializeUtil.unserialize();
        System.out.println(unserialize==unserialize1);
    }
6.通过ThreadLocal
public class Singleton_05 {
    private static Singleton_05 instance = null;
    private Singleton_05(){}

    private static final ThreadLocal<Singleton_05> theadLocalSingleton = new ThreadLocal<Singleton_05>(){
        @Override
        protected Singleton_05 initialValue() {
            return new Singleton_05();
        }
    };
    public static Singleton_05 getInstance(){
        return theadLocalSingleton.get();
    }

}
//创建单例模式 单线程
    @Test
    public void test05(){
        Singleton_05 instance = Singleton_05.getInstance();
        Singleton_05 instance1 = Singleton_05.getInstance();
        System.out.println(instance==instance1);

    }
    //创建单例模式  多线程
    @Test
    public void test005(){
        for (int i = 0; i < 20; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    Singleton_05 instance = Singleton_05.getInstance();
                    Singleton_05 instance1 = Singleton_05.getInstance();
                    System.out.println(Thread.currentThread().getName()+(instance==instance1)+" "+instance);
                }
            }).start();

        }
    }
7.CAS
CAS Compare And Swap 比较并替换。
CAS操作的就是乐观锁,每次不加锁而是假设没有冲突而去完成某项操作
如果因为冲突失败就重试,直到成功为止
public class Singleton_06 {
    //原子类
    private static final AtomicReference<Singleton_06> instance = new AtomicReference<>();
    private Singleton_06(){
        System.out.println("Singleton_06 loaded");
    }
    public static final Singleton_06 getInstance(){
        while (true) {
            Singleton_06 current = instance.get();
            if (current != null) {
                return current;
            }
            current = new Singleton_06();
            if (instance.compareAndSet(null, current)) {
                return current;
            }
        }
    }
}

 @Test
    public void test06() throws IOException {
        for (int i = 0; i < 20; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Singleton_06.getInstance());
                }
            }).start();

        }
         System.in.read();
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值