终于把单例模式讲详细了吧

单例模式

  • 饿汉式:
/**
 * 饿汉式单例(提前把对象创建)
 * 可能会浪费空间,提前把对象创建好了,但是不一定会用。
 */
public class Hungry {

    private Hungry(){

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

    public static Hungry getInstance(){

        return HUNGRY;
    }
}
  • 懒汉式

单线程下可用,多线程下不可用,存在多线程并发问题。

/**
 * 懒汉式单例(当使用时再创建对象)
 * 单线程下可用
 */
public class LazyMan {

    private LazyMan(){

    }
    private static LazyMan lazyMan;

    public LazyMan getInstnce(){
        if(lazyMan==null){
            lazyMan=new LazyMan();
        }
        return lazyMan;
    }
}

用多线程并发测试

public class LazyMan {

    private LazyMan(){

        System.out.println(Thread.currentThread().getName()+"ok");
    }
    private static LazyMan lazyMan;

    public static LazyMan getInstance(){
        if(lazyMan==null){
            lazyMan=new LazyMan();
        }
        return lazyMan;
    }

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

于是进阶为(加锁)DCL懒汉式(成为双重检测锁模式):

public class LazyMan {

    private LazyMan(){

        System.out.println(Thread.currentThread().getName()+"ok");
    }
    private static LazyMan lazyMan;

    //双重检测锁模式的懒汉式单例,DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan==null){
            synchronized (LazyMan.class){
                if(lazyMan==null){
                    lazyMan=new LazyMan();
                }
            }
        }
        return lazyMan;
    }

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

每一个线程开启,都只有一个单例对象。

DCL懒汉式单例再改进(添加volatle消除指令重排):

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){
                if(lazyMan==null){
                    lazyMan=new LazyMan();//不是原子性操作
                    /**不是原子性操作会发生以下三操作
                     * 1.分配内存空间
                     * 2.执行构造方法,初始化对象
                     * 3.把这个对象指向这个空间
                     *
                     *
                     * 会导致123或132顺序执行
                     * 若A线程顺序是132,又来了一个B线程,
                     * 则此时lazyMan没有完成构造,则会发生问题。(指令重排)
                     */
                }
            }
        }
        return lazyMan;
    }

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

  • 静态内部类
/**
 * 静态内部类
 */
public class Holder {

    private Holder(){

    }

    public static Holder getInstance(){
        return InnerClass.HOLDER;
    }
    public static class InnerClass{
        private static final Holder HOLDER=new Holder();
    }
}

以上单例方法都有问题,因为存在反射技术!!!

演示反射破解(两重检测锁模式)DCL进阶版懒汉式:

	//反射
    public static void main(String[] args) throws Exception {
        LazyMan instance1 = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyMan instance2 = declaredConstructor.newInstance();

        System.out.println(instance1);
        System.out.println(instance2);
    }

终于把单例模式讲详细了吧_第1张图片

进阶:在无参构造器中进行判断(成为三层检测锁模式),再执行:

public class LazyMan {

    private LazyMan(){
        synchronized (LazyMan.class){
            if(lazyMan!=null){
                throw new RuntimeException("不要试图使用反射破坏异常");
            }
        }
    }
    private volatile static LazyMan lazyMan;

    //双重检测锁模式的懒汉式单例,DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan==null){
            synchronized (LazyMan.class){
                if(lazyMan==null){
                    lazyMan=new LazyMan();//不是原子性操作
                    /**不是原子性操作会发生以下三操作
                     * 1.分配内存空间
                     * 2.执行构造方法,初始化对象
                     * 3.把这个对象指向这个空间
                     *
                     *
                     * 会导致123或132顺序执行
                     * 若A线程顺序是132,又来了一个B线程,
                     * 则此时lazyMan没有完成构造,则会发生问题。
                     */
                }
            }
        }
        return lazyMan;
    }

    //反射
    public static void main(String[] args) throws Exception {
        LazyMan instance1 = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyMan instance2 = declaredConstructor.newInstance();

        System.out.println(instance1);
        System.out.println(instance2);
    }
}

终于把单例模式讲详细了吧_第2张图片

使用反射再次破解时,把两个对象都用反射的方法newInstance() 创建出来:

//反射
    public static void main(String[] args) throws Exception {
//        LazyMan instance1 = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyMan instance1 = declaredConstructor.newInstance();
        LazyMan instance2 = declaredConstructor.newInstance();

        System.out.println(instance1);
        System.out.println(instance2);
    }

终于把单例模式讲详细了吧_第3张图片
进阶:采用红路灯(添加muzi属性),再执行:

public class LazyMan {

    private static boolean muzi=false;

    private LazyMan(){

        synchronized (LazyMan.class){
            if(muzi==false){
                muzi=true;
            } else{
                throw new RuntimeException("不要试图使用反射破坏异常");
            }
        }

    }
    private volatile static LazyMan lazyMan;

    //双重检测锁模式的懒汉式单例,DCL懒汉式
    public static LazyMan getInstance(){
        if(lazyMan==null){
            synchronized (LazyMan.class){
                if(lazyMan==null){
                    lazyMan=new LazyMan();//不是原子性操作
                    /**不是原子性操作会发生以下三操作
                     * 1.分配内存空间
                     * 2.执行构造方法,初始化对象
                     * 3.把这个对象指向这个空间
                     *
                     *
                     * 会导致123或132顺序执行
                     * 若A线程顺序是132,又来了一个B线程,
                     * 则此时lazyMan没有完成构造,则会发生问题。
                     */
                }
            }
        }
        return lazyMan;
    }

    //反射
    public static void main(String[] args) throws Exception {
//        LazyMan instance1 = LazyMan.getInstance();
        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        LazyMan instance1 = declaredConstructor.newInstance();
        LazyMan instance2 = declaredConstructor.newInstance();

        System.out.println(instance1);
        System.out.println(instance2);
    }
}

终于把单例模式讲详细了吧_第4张图片

反射破解(通过反射获取muzi属性,并修改):

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

        Constructor<LazyMan> declaredConstructor = LazyMan.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);

        LazyMan instance1 = declaredConstructor.newInstance();
        muzi.set(instance1,false);
        LazyMan instance2 = declaredConstructor.newInstance();


        System.out.println(instance1);
        System.out.println(instance2);
    }

终于把单例模式讲详细了吧_第5张图片

  • 枚举
/**
 * 枚举
 */
public enum  EnumSingle {
    INSTANCE;
    public EnumSingle getInstance(){
        return INSTANCE;
    }
}

你可能感兴趣的:(软件工程设计之23种设计模式)