23种设计模式---单例设计模式(精华)

单例模式

饿汉式:

package cn.tedu.single;
//饿汉式单例
public class Hungry {
    private Hungry(){
    }
    //会造成空间的浪费,开辟了空间,却没有使用
    private  final static Hungry HUNGRY= new Hungry();

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

懒汉式:

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

package cn.tedu.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){
                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<5;i++){
            new Thread(()->{
                lazyMan.getInstance();
            }).start();
        }
    }

}

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

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

  • Java 语言提供了volatile和 synchronized两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只串行执行。
  • 原子性就是指该操作是不可再分的。不论是多核还是单核,具有原子性的量,同一时刻只能有一个线程来对它进行操作。简而言之,在整个操作过程中不会被线程调度器中断的操作,都可认为是原子性。比如 a = 2;
package cn.tedu.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){//锁类
                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<5;i++){
            new Thread(()->{
                lazyMan.getInstance();
            }).start();
        }
    }

}

静态内部类

package cn.tedu.single;
//通过内部类
public class Inner {
    //构造器私有
    private Inner(){

    }
    public static Inner getInstance(){
        return InnerSon.inner;
    }
    public static class InnerSon{
        private static Inner inner = new Inner();
    }
}

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

package cn.tedu.single;

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

//单例不安全可以通过反射破环
public class Lazy {
    //设置标记
    private  static  boolean flag =false;
    private Lazy(){
        synchronized (Lazy.class){
            if (flag==false){
                flag=true;
            }else {
                throw  new RuntimeException("不要试图使用反射破环单例");//但是可以通过设置标记的布尔值
            }
        }
        System.out.println(Thread.currentThread().getName()+"OK");
    }
    private volatile   static Lazy lazy ;

    public static Lazy getLazy() {
        synchronized (Lazy.class){
            if (lazy==null){
                lazy=new Lazy();
            }
        }
        return lazy;
    }

    public static void main(String[] args) throws Exception {
        //        Lazy lazy = Lazy.getLazy();
        Field flag = Lazy.class.getDeclaredField("flag");
        flag.setAccessible(true);
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Lazy lazy1 = declaredConstructor.newInstance();
        flag.set(flag,false);
        Lazy lazy2 = declaredConstructor.newInstance();
        System.out.println(lazy1);
        System.out.println(lazy2);
        //仍然可以创建多个实例,道高一尺,魔高一丈

    }
}

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

package cn.tedu.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);// null  java.lang.NoSuchMethodException:
        declaredConstructor.setAccessible(true);
        EnumSingle instance1 = declaredConstructor.newInstance();
        System.out.println(instance);
        // Cannot reflectively create enum objects
        System.out.println(instance1);

    }
}

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

在这里插入图片描述

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

在这里插入图片描述

posted on 2020-06-14 20:47  liqiangbk  阅读(247)  评论(0编辑  收藏  举报

导航