单例模式详解

单例模式详解

饿汉式

package com.ph.single;

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

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

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


懒汉式

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

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;

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();
        }
    }
}


静态内部类

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方法中反射破解步骤)

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)

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

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

在这里插入图片描述

发现枚举是有参构造