Java实现单例模式总结

这种

1.单例模式简介

单例模式属于设计模式中的创建型模式。它主要用于一个类只能有一个实例的场景(想想一些只能有一个或者只需要一个的资源的场景),哪怕是多线程在访问这个实例,除此之外,需要提供一个全局访问它的点。

2.单例模式实现的方式

单例模式有很多实现方法,在这里只提供几种常见的,分别是饿汉、懒汉、静态内部类、枚举类

2.1 饿汉

public class HungrySingleton {
    // 饿汉模式,程序启动的时候直接加载 线程安全 线程安全由虚拟机保证
    // 这里使用final关键词是为了防止子类继承破坏单例
    private static final HungrySingleton instance = new HungrySingleton();
    // 构造方法设为私有,防止外部new创建
    private HungrySingleton() {}

    public static HungrySingleton getInstance() {
        return instance;
    }
}

优点:线程安全由虚拟机保证,可靠,外部需要使⽤的时候获取即可。

缺点:

(1)无论程序中是否使用都会在程序启动之初进⾏创建,有可能浪费空间(创建了但不使用)。

(2)这种形式的单例过多可能导致程序启动时间过长。

(3)私有化构造器方法不能防止外部创建会被暴力发射破坏。

2.2 懒汉

顾名思义,只有在真正使用的时候才会加载并创建该单例对象。

对于这种实现方式的单例模式,主要有两种,分别为

public class LazySingleton {
    // 这里使用volatile是为了防止instance = new LazySingleton();发生指令重排
    private static volatile LazySingleton instance;

    private LazySingleton() {};


    public static synchronized LazySingleton LazySingleton() {
        if (null != instance) {
            return instance;
        }
        instance = new LazySingleton();
        return instance;
    }

}

此方法实现主要通过方法锁的形式实现多线程安全,但这种加锁方式过于笨重,多线程在该方法上都是串行化,对于并发量高的常见不建议使用,以下是一种优化后的方案:

public class LazySingleton {
    // 这里使用volatile是为了防止instance = new LazySingleton();发生指令重排
    private static volatile LazySingleton instance;

    public static LazySingleton getInstance() {
        if (null != instance) {
            return instance;
        }

        synchronized (LazySingleton.class) {
            if (null == instance) {
                instance = new LazySingleton();
            }
        }

        return  instance;
    }
}

此方法实现也叫双重校验锁形式的单例,相较于把锁加载方法上,第一重校验单例是否为空就能处理大部分的并发请求,减小了锁的竞争。一定程度上优化了性能。

两个问题:

(1)为什么要双重校验:

有了第一重校验,还要有第二重校验的原因,可以考虑这样一个场景,即两个线程并发访问getInstance方法,且此时还没有创建instance对象,那么两个线程均能通过第一重校验锁,那么到了同步代码块,假设第一个线程先进去创建了单例对象(第二个线程阻塞等待),释放锁,第二个线程进去又一次创建了单例对象(没有第二重校验的话),破坏了单例的特性。

(2)volatile关键词的作用

这里使用volatile关键词的作用主要是为了防止instance = new LazySingleton();语句发生指令重排。这个语句如果发生指令重排,有可能先给instance引用赋值,再调用LazySingleton的构造方法,如果这两个操作之间有别的线程调用getInstance方法,则会返回一个还没有初始化完的LazySingleton对象。

优点:线程安全,懒加载,一定程度上可以节省内存资源。

缺点:加锁的⽅式实现线程安全耗费性能。私有构造器方法防止外部创建会被暴力发射破坏

2.3 枚举

public enum EnumSingleton {
    INSTACE;
}

使用

public class Test {
    public static void main(String[] args) {
        System.out.println(EnumSingleton.INSTACE.toString());
    }
}

优点:线程安全,其线程安全由虚拟机保证,而且可以防止暴力反射破坏单例,总得来说也是一种比较推荐的方式

缺点:饿汉式实现思想。有可能浪费空间,但设计合理可以避免这个问题。

2.4 静态内部类

public class InnerSingleton {

    private InnerSingleton() {}

    private static class InnerClass {
        private static InnerSingleton instance = new InnerSingleton();
    }

    public static InnerSingleton getInstance() {
        return InnerClass.instance;
    }
}

优点:不用加锁实现两线程安全,其线程安全由虚拟机保证,且也实现了懒加载的思想,用的时候再创建,这是一种比较推荐的单例模式实现方式。

缺点:私有化构造器方法不能防止外部创建会被暴力发射破坏。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值