前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >单例模式详解

单例模式详解

作者头像
崔笑颜
发布2020-06-28 11:48:02
5930
发布2020-06-28 11:48:02
举报

饿汉式

代码语言:javascript
复制
package com.ph.single;

//饿汉式单例模式
public class Hungry {
    
    //可能会浪费空间,开辟了空间,却没有使用
    private Hungry(){

    }
    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance(){
        return HUNGRY;
    }
}

懒汉式

存在多线程并发模式,后面的DCL懒汉式解决并发问题

代码语言:javascript
复制
package com.ph.single;

public class LazyMan {
    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+"OK");
    }

    private static LazyMan lazyMan;


    //双重检测锁模式的  懒汉式单例    DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan==null){
            lazyMan = new LazyMan();//不是一个原子性操作
        }
        return lazyMan;
    }
    /*
    * 1.分配内存空间
    * 2、执行构造方法,初始化对象
    * 3、把这个对象指向者个空间
    *
    * 123
    * 132 A
    *
    *     B //此时lazyMan还没有完成构造
    *
    * */


    //多线程并发
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }
    }
}

DCL懒汉式:双重检测锁模式的懒汉式单例

注意:synchronized 解决并发问题,但是因为lazyMan = new LazyMan();不是原子性操作(可以分割,见代码注释),可能发生指令重排序的问题,通过volatil来解决

Java 语言提供了 volatile和 synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。 原子性就是指该操作是不可再分的。不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。比如 a = 1;

代码语言:javascript
复制
package com.ph.single;

public class LazyMan {
    private LazyMan(){
        System.out.println(Thread.currentThread().getName()+"OK");
    }

    private volatile static LazyMan lazyMan;


    //双重检测锁模式的  懒汉式单例    DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan==null){
            synchronized (LazyMan.class){//synchronized加锁解决多线程下的问题
                if(lazyMan == null){
                    lazyMan = new LazyMan();//不是一个原子性操作
                }
            }

        }
        return lazyMan;
    }
    /*
    * 1.分配内存空间
    * 2、执行构造方法,初始化对象
    * 3、把这个对象指向者个空间
    *
    * 123
    * 132 A
    *
    *     B //此时lazyMan还没有完成构造
    *
    * */


    //多线程并发
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }
    }
}

静态内部类

代码语言:javascript
复制
package com.ph.single;


//静态内部类
public class Holder {

    //构造器私有
    private Holder(){

    }
    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }

    public static class InnerClass{
        private static final Holder HOLDER = new Holder();

    }
}

单例不安全,反射破坏(见注释及main方法中反射破解步骤)

代码语言:javascript
复制
package com.ph.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

//单例懒汉式
//懒汉式单例

public class LazyMan {
    private static boolean qingjiang = false;//红绿等解决通过反射创建对象(反编译可以破解该方法)
    private LazyMan(){
        synchronized (LazyMan.class){
            if (qingjiang==false){
                qingjiang = true;
            }else{
                throw new RuntimeException("不要试图使用反射破坏单例");
            }

        }
        System.out.println(Thread.currentThread().getName()+"OK");
    }

    private volatile static LazyMan lazyMan;//volatile避免指令重排


    //双重检测锁模式的  懒汉式单例    DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan==null){
            lazyMan = new LazyMan();//不是一个原子性操作
        }
        return lazyMan;
    }

//反射!
public static void main(String[] args) throws Exception {
    //LazyMan instance = LazyMan.getInstance();
    Field qingjiang = LazyMan.class.getDeclaredField("qingjiang");
    qingjiang.setAccessible(true);

    Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
    declaredConstructor.setAccessible(true);//无视私有的构造器
    LazyMan instance1 = declaredConstructor.newInstance();
    qingjiang.set(instance1,false);
    System.out.println(instance1);
    LazyMan instance2 = declaredConstructor.newInstance();

    System.out.println(instance2);

}

    /*
    * 1.分配内存空间
    * 2、执行构造方法,初始化对象
    * 3、把这个对象指向者个空间
    *
    * 123
    * 132 A
    *
    *     B //此时lazyMan还没有完成构造
    *
    * */


    //多线程并发
   /* public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                LazyMan.getInstance();
            }).start();
        }
    }*/
}

枚举:通过反射破解枚举发现不成功: 1、普通的反编译会欺骗开发者,说enum枚举是无参构造 2、实际enum为有参构造(见后面); 3、通过反射破解枚举会发现抛出异常 Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects at java.lang.reflect.Constructor.newInstance(Constructor.java:417) at com.ph.single.Test.main(EnumSingle.java:19)

代码语言:javascript
复制
package com.ph.single;


import java.lang.reflect.Constructor;

//enmu是什么?本身也是一个class类
public enum EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

class Test{
    public static void main(String[] args) throws Exception {
        EnumSingle instance = EnumSingle.INSTANCE;
        Constructor<EnumSingle> declaredConstructor = EnumSingle.class.getDeclaredConstructor(String.class,int.class);
        declaredConstructor.setAccessible(true);
        EnumSingle instance2 = declaredConstructor.newInstance();

        //java.lang.NoSuchMethodException: com.ph.single.EnumSingle.<init>()
        System.out.println(instance);
        System.out.println(instance2);

    }
}

通过idea和jdk自带的反编译枚举如下:

img
img

通过jad反编译枚举的代码如下

在这里插入图片描述
在这里插入图片描述

发现枚举是有参构造

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020-06-24,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 饿汉式
  • 懒汉式
    • 存在多线程并发模式,后面的DCL懒汉式解决并发问题
      • DCL懒汉式:双重检测锁模式的懒汉式单例
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档