五种单例模式

单例模式

一、概述

单例模式的定义就是确保某一个类只有一个实例,并且提供一个全局访问点。属于设计模式三大类中的创建型模式。 单例模式具有典型的三个特点

  • 只有一个实例。

  • 自我实例化。

  • 提供全局访问点。

 

二、优缺点

  • 优点:由于单例模式只生成了一个实例,所以能够节约系统资源,减少性能开销,提高系统效率,同时也能够严格控制客户对它的访问。

  • 缺点:也正是因为系统中只有一个实例,这样就导致了单例类的职责过重,违背了“单一职责原则”,同时也没有抽象类,这样扩展起来有一定的困难。

三、示例

饿汉式

//单例的思想: 构造器私有
public class SingletonDemo1 {
​
    private SingletonDemo1(){
​
    }
    // 但容量浪费空间
    private final static SingletonDemo1 singletonDemo1 = new SingletonDemo1();
​
​
    public static SingletonDemo1 getInstance(){
        return singletonDemo1;
    }
    
}

懒汉式

package demo.singleton;
//在多线程下会失效
public class SingletonDemo2 {
​
    private SingletonDemo2(){
        System.out.println(Thread.currentThread().getName() + "111");
    }
​
    private static SingletonDemo2 singletonDemo2;
​
    public static SingletonDemo2 getInstance(){
​
        if (singletonDemo2 == null){
            singletonDemo2 = new SingletonDemo2();
        }
        return singletonDemo2;
    }
​
    public static void main(String[] args) {
        // 多线程下单例失效
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                SingletonDemo2.getInstance();
            }).start();
        }
    }
​
}
Thread-1111
Thread-2111
Thread-0111

懒汉式(双重检测锁式)

package demo.singleton;
​
​
// java创建对象总分为三步
// 1.分配内存空间
// 2.执行构造函数,创建对象
// 3.将对象指向空间
​
public class SingletonDemo3 {
​
    private SingletonDemo3(){
        System.out.println(Thread.currentThread().getName() + "111");
    }
​
    private static volatile SingletonDemo3 singletonDemo3;
​
    public static SingletonDemo3 getInstance(){
​
        if (singletonDemo3 == null){
            // 细粒度加锁
            synchronized (SingletonDemo3.class){
                if (singletonDemo3 == null){
                    singletonDemo3 = new SingletonDemo3();
                }
            }
​
        }
        return singletonDemo3;
    }
​
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                SingletonDemo3.getInstance();
            }).start();
        }
    }
​
}

思考为什么需要加volatile?

 java创建对象总分为三步
 1.分配内存空间
 2.执行构造函数,创建对象
 3.将对象指向空间
 在多线程模式下,因为指令重排A线程进来先执行的13,对象并没有真正的被创建出来,但B线程此时进来了,发现对象已经存在,此时B线程拿到的对象就不是完整的,而volatile就是为了防止指令重排。

思考:双重检测锁式真的是安全的吗?答案是不安全的,可以通过反射去破坏

示例:

package demo.singleton;
​
​
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
​
public class SingletonDemo3 {
​
    private SingletonDemo3(){
​
    }
​
    private static volatile SingletonDemo3 singletonDemo3;
​
    public static SingletonDemo3 getInstance(){
​
        if (singletonDemo3 == null){
            // 细粒度加锁
            synchronized (SingletonDemo3.class){
                if (singletonDemo3 == null){
                    singletonDemo3 = new SingletonDemo3();
                }
            }
​
        }
        return singletonDemo3;
    }
​
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
​
        //通过反射创建对象
        Constructor<SingletonDemo3> declaredConstructor = SingletonDemo3.class.getDeclaredConstructor();
        SingletonDemo3 singletonDemo3 = declaredConstructor.newInstance();
        SingletonDemo3 singletonDemo = declaredConstructor.newInstance();
​
        System.out.println(singletonDemo.hashCode());
        System.out.println(singletonDemo3.hashCode());
​
​
    }
​
}
结果:
21685669
2133927002

由此可以看出两个对象的hashCode是不一样的,所以不安全;

怎么解决?==静态内部类式==

静态内部类式

package demo.singleton;
​
​
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
​
public class SingletonDemo4 {
​
    private static boolean flag = false;
​
    private SingletonDemo4(){
​
        synchronized (SingletonDemo4.class){
            if (!flag){
                flag = true;
            }else {
                throw new RuntimeException("不要破坏单例");
            }
        }
    }
​
    private static volatile SingletonDemo4 singletonDemo4;
​
    public static SingletonDemo4 getInstance(){
​
        if (singletonDemo4 == null){
            // 细粒度加锁
            synchronized (SingletonDemo4.class){
                if (singletonDemo4 == null){
                    singletonDemo4 = new SingletonDemo4();
                }
            }
​
        }
        return singletonDemo4;
    }
​
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
​
        //通过反射创建对象
        Constructor<SingletonDemo4> declaredConstructor = SingletonDemo4.class.getDeclaredConstructor();
        SingletonDemo4 singletonDemo3 = declaredConstructor.newInstance();
        SingletonDemo4 singletonDemo = declaredConstructor.newInstance();
​
        System.out.println(singletonDemo.hashCode());
        System.out.println(singletonDemo3.hashCode());
​
​
    }
​
}
结果:
Exception in thread "main" java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
    at demo.singleton.SingletonDemo4.main(SingletonDemo4.java:43)
Caused by: java.lang.RuntimeException: 不要破坏单例
    at demo.singleton.SingletonDemo4.<init>(SingletonDemo4.java:17)
    ... 5 more

思考:这样就可以保证安全吗?假如对方知道了flag这个字段呢?

示例:

package demo.singleton;
​
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
​
public class SingletonDemo4 {
​
    private static boolean flag = false;
​
    private SingletonDemo4(){
​
        synchronized (SingletonDemo4.class){
            if (!flag){
                flag = true;
            }else {
                throw new RuntimeException("不要破坏单例");
            }
        }
    }
​
    private static volatile SingletonDemo4 singletonDemo4;
​
    public static SingletonDemo4 getInstance(){
​
        if (singletonDemo4 == null){
            // 细粒度加锁
            synchronized (SingletonDemo4.class){
                if (singletonDemo4 == null){
                    singletonDemo4 = new SingletonDemo4();
                }
            }
​
        }
        return singletonDemo4;
    }
​
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException {
​
        //通过反射创建对象
        Constructor<SingletonDemo4> declaredConstructor = SingletonDemo4.class.getDeclaredConstructor();
        declaredConstructor.setAccessible(true);//1、关闭安全检查就可以达到提升反射速度2、访问私有private变量
        SingletonDemo4 singletonDemo4 = declaredConstructor.newInstance();
        Field flag = SingletonDemo4.class.getDeclaredField("flag");
        flag.setAccessible(true);
        flag.set(singletonDemo4,false);
        SingletonDemo4 singletonDemo = declaredConstructor.newInstance();
​
        System.out.println(singletonDemo.hashCode());
        System.out.println(singletonDemo4.hashCode());
​
​
    }
​
}
结果:
2133927002
1836019240

我们又把单例破坏了,怎么办呢?枚举单例

枚举单例

package demo.singleton;
​
​
public class SingletonDemo5 {
​
    private SingletonDemo5(){
​
    }
​
    //定义一个枚举类
     enum SingletonEnum{
        //创建一个枚举对象,该对象天生为单例
        INSTANCE;
        private SingletonDemo5 singletonDemo5;
        //私有化枚举的构造函数
        SingletonEnum(){
            singletonDemo5=new SingletonDemo5();
        }
        public SingletonDemo5 getInstnce(){
            return singletonDemo5;
        }
    }
​
    public static SingletonDemo5 getInstance(){
        return SingletonEnum.INSTANCE.getInstnce();
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值