欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

单例模式学习

程序员文章站 2022-07-06 10:10:21
单例模式学习 1 饿汉式单例模式 还没用就创建了对象,可能会浪费空间 2 懒汉式单例模式 无线程锁 java package main.java.com.yuehun.singleton; / main.java.com.yuehun.singleton @author yuehun Created ......

单例模式学习

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

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