用户登录
用户注册

分享至

单例模式学习

  • 作者: 花落季16238097
  • 来源: 51数据库
  • 2020-08-06

单例模式学习

1 饿汉式单例模式

package main.java.com.yuehun.singleton;

/**
 * main.java.com.yuehun.singleton
 *
 * @author yuehun
 * Created on 2020/4/27.
 */

// 饿汉式单例模式
public class Hungry {
    private Hungry(){}

    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance()
    {
        return HUNGRY;
    }
}
  • 还没用就创建了对象,可能会浪费空间

2 懒汉式单例模式

无线程锁

package main.java.com.yuehun.singleton;

/**
 * main.java.com.yuehun.singleton
 *
 * @author yuehun
 * Created on 2020/4/27.
 */

// 懒汉式单例模式
public class Lazy {
    private Lazy(){
        System.out.println(Thread.currentThread().getName() + "OK");
    }

    private static Lazy lazy = null;

    public static Lazy getInstance()
    {
        if (lazy == null)
        {
            lazy = new Lazy();
        }
        return lazy;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++)
        {
            new Thread(Lazy::getInstance).start();
        }
    }
}
  • 单线程下可以,但是多线程不安全

加线程锁

package main.java.com.yuehun.singleton;

/**
 * main.java.com.yuehun.singleton
 *
 * @author yuehun
 * Created on 2020/4/27.
 */

// 懒汉式单例模式
public class Lazy {
    private Lazy(){
        System.out.println(Thread.currentThread().getName() + " OK");
    }

    private volatile static Lazy lazy = null;

    // 双重检测锁模式的懒汉式单例模式, DCL 懒汉式
    public static Lazy getInstance()
    {
        if (lazy == null) {
            synchronized (Lazy.class) {
                if (lazy == null) {
                    lazy = new Lazy(); // 不是一个原子性操作
                    /**
                     * 1 分配内存空间
                     * 2 执行构造方法,初始化对象
                     * 3 把这个对象指向这个空间
                     *
                     * 可能会发生指令重排!!!
                     */
                }
            }
        }
        return lazy;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 10; i++)
        {
            new Thread(Lazy::getInstance).start();
        }
    }
}

3 单例不是安全的

使用反射破坏单例模式

  • Java 里面有个东西叫反射
  • 比如:
package main.java.com.yuehun.singleton;

import java.lang.reflect.Constructor;

/**
 * main.java.com.yuehun.singleton
 *
 * @author yuehun
 * Created on 2020/4/27.
 */

// 饿汉式单例模式
public class Hungry {
    private Hungry(){}

    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance()
    {
        return HUNGRY;
    }

    public static void main(String[] args) throws Exception {
        Hungry instance = new Hungry();

        Constructor<Hungry> declaredConstructor = Hungry.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Hungry instance2 = declaredConstructor.newInstance();

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

阻止反射破坏

阻止反射的破坏

package main.java.com.yuehun.singleton;

import java.lang.reflect.Constructor;

/**
 * main.java.com.yuehun.singleton
 *
 * @author yuehun
 * Created on 2020/4/27.
 */

// 饿汉式单例模式
public class Hungry {
    private Hungry(){
        synchronized (Hungry.class)
        {
            if (HUNGRY != null)
            {
                throw new RuntimeException("不要试图使用反射破坏单例模式");
            }
        }
    }

    private final static Hungry HUNGRY = new Hungry();

    public static Hungry getInstance()
    {
        return HUNGRY;
    }

    public static void main(String[] args) throws Exception {
        Hungry instance = new Hungry();

        Constructor<Hungry> declaredConstructor = Hungry.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Hungry instance2 = declaredConstructor.newInstance();

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

还有一种反射破坏

  • 如果两个对象都是通过反射构造的,就不会触发异常
package main.java.com.yuehun.singleton;

import java.lang.reflect.Constructor;

/**
 * main.java.com.yuehun.singleton
 *
 * @author yuehun
 * Created on 2020/4/27.
 */

// 懒汉式单例模式
public class Lazy {
//    private static boolean flag = false;

    private Lazy()
    {
        synchronized (Lazy.class)
        {
//            if (!flag)
//                flag = true;
//            else {
                if (lazy != null) {
                    throw new RuntimeException("不要试图使用反射破坏单例模式");
//                }
            }
        }
    }

    private volatile static Lazy lazy = null;

    // 双重检测锁模式的懒汉式单例模式, DCL 懒汉式
    public static Lazy getInstance()
    {
        if (lazy == null)
        {
            synchronized (Lazy.class)
            {
                if (lazy == null)
                {
                    lazy = new Lazy(); // 不是一个原子性操作
                    /**
                     * 1 分配内存空间
                     * 2 执行构造方法,初始化对象
                     * 3 把这个对象指向这个空间
                     *
                     * 可能会发生指令重排!!!
                     */
                }
            }
        }
        return lazy;
    }

    public static void main(String[] args) throws Exception
    {
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Lazy instance = declaredConstructor.newInstance();
        Lazy instance2 = declaredConstructor.newInstance();

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

又有一种解决方法

package main.java.com.yuehun.singleton;

import java.lang.reflect.Constructor;

/**
 * main.java.com.yuehun.singleton
 *
 * @author yuehun
 * Created on 2020/4/27.
 */

// 懒汉式单例模式
public class Lazy {
    private static boolean flag = false;

    private Lazy()
    {
        synchronized (Lazy.class)
        {
            if (!flag)
                flag = true;
            else
                throw new RuntimeException("不要试图使用反射破坏单例模式");
        }
    }

    private volatile static Lazy lazy = null;

    // 双重检测锁模式的懒汉式单例模式, DCL 懒汉式
    public static Lazy getInstance()
    {
        if (lazy == null)
        {
            synchronized (Lazy.class)
            {
                if (lazy == null)
                {
                    lazy = new Lazy(); // 不是一个原子性操作
                    /**
                     * 1 分配内存空间
                     * 2 执行构造方法,初始化对象
                     * 3 把这个对象指向这个空间
                     *
                     * 可能会发生指令重排!!!
                     */
                }
            }
        }
        return lazy;
    }

    public static void main(String[] args) throws Exception
    {
        Constructor<Lazy> declaredConstructor = Lazy.class.getDeclaredConstructor(null);
        declaredConstructor.setAccessible(true);
        Lazy instance = declaredConstructor.newInstance();
        Lazy instance2 = declaredConstructor.newInstance();

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

破坏者又来了

  • 可以通过反射获取到我们设置的这个标志位,在以后每一次创建对象的时候修改它
package main.java.com.yuehun.singleton;

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

/**
 * main.java.com.yuehun.singleton
 *
 * @author yuehun
 * Created on 2020/4/27.
 */

// 懒汉式单例模式
public class Lazy {
    private static boolean flag = false;

    private Lazy()
    {
        synchronized (Lazy.class)
        {
            if (!flag)
                flag = true;
            else
                throw new RuntimeException("不要试图使用反射破坏单例模式");
        }
    }

    private volatile static Lazy lazy = null;

    // 双重检测锁模式的懒汉式单例模式, DCL 懒汉式
    public static Lazy getInstance()
    {
        if (lazy == null)
        {
            synchronized (Lazy.class)
            {
                if (lazy == null)
                {
                    lazy = new Lazy(); // 不是一个原子性操作
                    /**
                     * 1 分配内存空间
                     * 2 执行构造方法,初始化对象
                     * 3 把这个对象指向这个空间
                     *
                     * 可能会发生指令重排!!!
                     */
                }
            }
        }
        return lazy;
    }

    public static void main(String[] args) throws Exception
    {
        Field flag = Lazy.class.getDeclaredField("flag");
        flag.setAccessible(true);

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

        Lazy instance = declaredConstructor.newInstance();
        
        flag.set(instance, false);
        Lazy instance2 = declaredConstructor.newInstance();

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

道高一尺,魔高一丈

4 枚举解决

  • enum 本身也是一个 Class 类
package main.java.com.yuehun.singleton;

import java.lang.reflect.Constructor;

/**
 * main.java.com.yuehun.singleton
 *
 * @author yuehun
 * Created on 2020/4/27.
 */
public enum EnumSingleton {
    INSTANCE;

    public EnumSingleton getInstance()
    {
        return INSTANCE;
    }
}

class Test {
    public static void main(String[] args) throws Exception {
        EnumSingleton instance1 = EnumSingleton.INSTANCE;

        Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
        declaredConstructor.setAccessible(true);
        EnumSingleton instance2 = declaredConstructor.newInstance();

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

到此,阻止反射破坏就成功了!

原文链接:http://www.cnblogs.com/yuehungege/p/studynotes_singletonpatterns.html

软件
前端设计
程序设计
Java相关