设计模式-11 单例模式

1.什么是单例模式

单例模式确保一个类只有一个实例,并提供一个全局访问点,实现单例模式的方法是私有化构造函数,通过getInstance()方法实例化对象,并返回这个实例

2.单例模式优缺点

2.1 优点

  • 单例类只有一个实例
  • 共享资源,全局使用
  • 节省创建时间,提高性能

2.2 缺点

可能存在线程不安全的问题

3.单例写法

  • 饿汉
  • 懒汉(非线程安全)
  • 懒汉(线程安全)
  • 双重校验锁
  • 静态内部类
  • 枚举
  • 容器类管理
  • 静态块初始化

3.1 饿汉式

  • 优点:先天性线程安全 为什么先天性 当类加载的时候 就被创建该对象 。
  • 缺点:如果项目使用过多的饿汉式会发生问题,项目在启动的时候会边的非常慢、存放在方法区占用内存比较大
public class SingletonV1 {
    private static SingletonV1 singletonV1 = new SingletonV1();

    //1.单例模式是否可以让程序员初始化
    private SingletonV1() {
    }

    /**
     * 返回该对象的实例
     *
     * @return
     */
    public static SingletonV1 getInstance() {
        return singletonV1;
    }
}

3.2 懒汉式(线程不安全)

public class SingletonV2 {
    // 懒汉式 当真正需要使用该对象的时候才会被初始化 线程安全问题 但是效率非常低
    private static SingletonV2 singletonV2;

    //1.单例模式是否可以让程序猿初始化
    private SingletonV2() {
    }

    /**
     * 线程安全问题 在多线程情况下 可能会被初始化多次
     * @return
     */
    public static SingletonV2 getSingletonV2() {
        //当第一次singletonV2 等于null 情况 才会被初始化
        try {
            Thread.sleep(3000);
        } catch (Exception e) {
        }
        if (singletonV2 == null) {
            singletonV2 = new SingletonV2();
        }
        return singletonV2;
    }
}
  • 测试线程安全性
public class SingletonV2Test {
    public static void main(String[] args) {
        // 如何去模拟高并发情况下 懒汉式线程安全问题
        for (int i = 0; i < 10; i++) {
            new Thread(new Runnable() {
                public void run() {
                    SingletonV2 singletonV1 = SingletonV2.getSingletonV2();
                    System.out.println(Thread.currentThread().getName() + "," + singletonV1);
                }
            }).start();
        }
    }
}

线程不安全效果

3.3 懒汉式(线程安全)

加入synchronized关键字

public synchronized static SingletonSafeV2 getSingletonV2() {
        //当第一次singletonV2 等于null 情况 才会被初始化
        try {
            Thread.sleep(3000);
        } catch (Exception e) {
        }
        if (singletonV2 == null) {
            singletonV2 = new SingletonSafeV2();
        }
        return singletonV2;
    }

3.4 双重检验锁

双重检验锁 解决懒汉式 读和写都加上锁的问题 缺点 第一次创建对象可能会比较慢

public class SingletonV3 {
    /**
     * volatile 防止重排序 java内存模型 增加可见性
     */
    private volatile static SingletonV3 singletonV3;

    // 双重检验锁 解决懒汉式 读和写都加上锁的问题 缺点 第一次创建对象可能会比较慢
    // 如何解决 写和读 都不加锁 还能够保证唯一性 线程安全问题
    private SingletonV3() throws Exception {
        if (singletonV3 != null) {
            throw new Exception("对象已经被初始化..");
        }
        System.out.println("SingletonV3被初始化...");
    }

//    /**
//     * 读的不加锁的,写的时候才会加锁。。
//     */
    public static SingletonV3 getSingletonV3() throws Exception {
        // 当多个线程 同时在可能new 对象的时候 才会加锁,保证线程问题。
        if (singletonV3 == null) {
            try {
                Thread.sleep(3000);
            } catch (Exception e) {
            }
            synchronized (SingletonV3.class) {
                if (singletonV3 == null) { // 当前线程已经获取到锁的呢,在判断一下该对象是否已经初始化过,没有初始化过的 创建
                    singletonV3 = new SingletonV3();
                }
            }
        }
        return singletonV3;
    }
    // 双重检验锁目的什么? 解决懒汉式获取对象效率问题。
    /**
     *  如果 if singletonV3 == null 比较巧  正好有10个线程进入到呢 25行代码获取锁
     *  因为synchronized 保证线程问题,只需要有一个线程获取锁,
     */

}

3.5 静态内部类

public class SingletonV4 {
    private SingletonV4() {
        System.out.println("构造函数被初始化...");
    }

    public static SingletonV4 getInstance() {
        return SingletonV5Utils.singletonV4;
    }

    // 在类里面嵌套的
    private static class SingletonV5Utils {
        private static final SingletonV4 singletonV4 = new SingletonV4();
    }

    /**
     * 内部类在调用的时候才会初始化singletonV5
     * static 静态 保证唯一
     * @param args
     */
    // 静态内部类特征:继承懒汉式和饿汉式优点、同时解决双重检验锁第一次加载慢的问题 读和写都不需要同步效率非常高...
    public static void main(String[] args) {
        System.out.println("项目启动成功...");
        SingletonV4 instance1 = SingletonV4.getInstance();
        SingletonV4 instance2 = SingletonV4.getInstance();
        System.out.println(instance1 == instance2);
    }
}

3.6 枚举方式

public enum EnumSingleton {
    INSTANCE;

    // 枚举能够绝对有效的防止实例化多次,和防止反射和序列化破解
    public void add() {
        System.out.println("add方法...");
    }
    // 枚举是如何初始化的? 反序列化底层是如何解决防止单例被破解。
}
public class EnumSingletonTest {
    public static void main(String[] args) {
        EnumSingleton instance1 = EnumSingleton.INSTANCE;
        EnumSingleton instance2 = EnumSingleton.INSTANCE;
        System.out.println(instance1 == instance2);
        instance1.add();
    }
}

3.7 容器管理

这种使用SingletonManager 将多种单例类统一管理,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。

public class SingletonManager {
    private static Map<String, Object> objMap = new HashMap<String, Object>();
    public static void registerService(String key, Object instance) {
        if (!objMap.containsKey(key)) {
            objMap.put(key, instance);
        }
    }
    public static Object getService(String key) {
        {
            return objMap.get(key);
        }
    }
}

4.如何防止破坏单例

4.1 反射技术

虽然单例通过私有构造函数,可以实现防止程序员初始化对象,但是还可以通过反射和序列化技术破解单例。

// 1. 使用懒汉式创建对象
SingletonV3 instance1 = SingletonV3.getInstance();
// 2. 使用Java反射技术初始化对象 执行无参构造函数
Constructor<SingletonV3> declaredConstructor = SingletonV3.class.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
SingletonV3 instance2 = declaredConstructor.newInstance();
System.out.println(instance1 == instance2);

4.2 使用序列化技术破解单例

Singleton instance = Singleton.getInstance();
FileOutputStream fos = new FileOutputStream("E:\\code\\Singleton.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(instance);
oos.flush();
oos.close();

FileInputStream fis = new FileInputStream("E:\\code\\Singleton.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
Singleton singleton2 = (Singleton) ois.readObject();
System.out.println(singleton2==instance)

5.防止破解

5.1 反射技术

private SingletonV3() throws Exception {
    synchronized (SingletonV3.class) {
        if (singletonV3 != null) {
            throw new Exception("该对象已经初始化..");
        }
        System.out.println("执行SingletonV3无参构造函数...");
    }
}

5.2 序列化

//返回序列化获取对象 ,保证为单例
public Object readResolve() {
    return singletonV3;
}
运行环境:Windows NT/2000/XP/2003/Vista/7 主要功能 【标准点击模式】 ·模拟鼠标点击 ·任意设置点击位置(F12/F11键定位鼠标点击位置) ·任意设置点击时间间隔(在两个定义的时间内点击) ·任意设置点击顺序(按自定义的位置,顺序点击或随机点击) ·任意设置点击方式(鼠标单击或双击,左或或右键点击) ·每个点击位置的点击方式和点击间隔支持单独设定 ·可以设定在点击若干秒后停止点击,停止点击后的若干秒再次开始点击。 ·鼠标移动检测点击等待功能 ·点击后可以回到原来的鼠标位置 ·缩到托盘时,左键显示窗口,右键显示快捷菜单 ·自动关闭垃圾窗口功能 ·可将不同的点击设置存为不同的方案,大大方便了设置 ·可以设定循环点击 ·定时点击功能 ·点击后输入设定的文字 【游戏点击模式】 ·当前位置快速点击功能(游戏模式) ·开始/停止点击可自定义热键 ·支持每秒1-100次点击设定 ·左/右键点击设定 ·缩到托盘时,左键显示窗口,右键显示快捷菜单 ·进程随机标题功能以防止本软件被其它软件屏蔽 一、简介:   网赚的时候是不是很希望自动点击广告条?鼠标点击精灵能帮你的忙!你可以随意设置点击时间间隔与点击的方式,并能很轻易地捕捉想要点击的位置。另外,可以在同一位置快速多次点击(游戏点击模式)。   绿色软件,无需安装。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值