为什么说单例模式的饿汉式是线程安全的?

2022-08-10 12:07:12

一、类加载的方式是按需加载,且只加载一次

因此,在上述单例类被加载时,就会实例化一个对象并交给自己的引用,供系统使用。单例就是该类只能返回一个实例。

换句话说,在线程访问单例对象之前就已经创建好了。再加上,由于一个类在整个生命周期中只会被加载一次,因此该单例类只会创建一个实例。

也就是说,线程每次都只能也必定只可以拿到这个唯一的对象。即饿汉式单例天生就是线程安全的。

二、单例模式几种实现

单例模式特点

私有构造方法private SingleTon1()
私有静态全局变量private static SingleTon1 singleton = new SingleTon1()
公有静态方法public static getInstance()

instance为什么一定要是static的?

1.通过静态的类方法(getInstance) 获取instance,该方法是静态方法,instance由该方法返回(被该方法使用),如果instance非静态,无法被getInstance调用;

2.instance需要在调用getInstance时候被初始化,只有static的成员才能在没有创建对象时进行初始化。且类的静态成员在类第一次被使用时初始化后就不会再被初始化,保证了单例;

3.static类型的instance存在静态存储区,每次调用时,都指向的同一个对象。其实存放在静态区中的是引用,而不是对象。而对象是存放在堆中的。

单例模式的构造方法为什么私有?

1.设置private以后,每次new对象的时候都要调用构造方法。而private的权限是当前类,那么其他类new对象的时候一定会失败。
2.设置成private是考虑封装性,防止在外部类中进行初始化,也就不是单例了。

饿汉式:

//基于JVM的类加载器机制避免了多线程的同步问题,对象在类装载时就实例化publicclassSingleTon1(){privateSingleTon1(){}privatestaticSingleTon1 singleton=newSingleTon1();publicstaticgetInstance(){return singleton;}}

懒汉式:

//能够在getInstance()时再创建对象,所以称为懒汉式。//这种实现最大的问题就是不支持多线程。因为没有加锁同步。publicclassSingleTon2(){privateSingleTon2(){}privatestaticSingleTon2 singleton2=null;publicstaticgetInstance(){if(singleton2==null){
	   singleton2=newSingleTon2();}return singleton2;}}

加同步锁synchronized的懒汉模式:

//除第一次使用,getInstance()需要同步,后面getInstance()不需要同步;每次同步,效率很低。publicclassSingleTon3(){privateSingleTon3(){}privatestaticSingleTon3 singleton3=null;publicsynchronizedstaticgetInstance(){if(singleton3==null){
	   singleton3=newSingleTon3();}return singleton3;}}

双重锁模式:

//安全且在多线程情况下能保持高性能。//实例变量需要加volatile 关键字保证易变可见性publicclassSingleTon4{privateSingleTon4(){}privatevolatilestaticSingleTon4 singleton4=null;publicstaticSingleTon4getSingleton(){if(singleton4==null){synchronized(SingleTon4.class){if(singleton4==null){
 		      singleton4=newSingleTon3();}}}return singleton4;}}

静态内部类模式:

//利用了JVM类加载机制来保证初始化实例对象时只有一个线程,//静态内部类SingletonHolder类只有第一次调用getInstance方法时,才会装载从而实例化对象publicclassSingleton5{privateSingleTon5(){}privatestaticclassSingletonHolder{privatestaticfinalSingleton5=newSingleton5();}publicstaticfinalSingleton5getInstance(){returnSingletonHolder.Singleton5;}}

三、单例模式VS静态类(静态属性/方法)

把类中所有属性/方法定义成静态也可以实现"单例"。 静态类不用实例化就可以使用,虽然使用比较方便,但失去了面向对象的一些优点,适用于一些过程简单且固定、不需要扩展变化、不需要维护任何状态的类方法,如java.lang.Math,里面每种计算方法基本都是固定不变的。那为什么需要用"NEW"单例模式,而不把类中所有属性/方法定义成静态的?
单例模式保证一个类对象实例的唯一性,有面向对象的特性,虽然扩展不容易,但还是可以被继承(protected权限的构造方法)、重写方法等。

四、Java反射攻击破坏单例模式

给实例构造函数protected或private权限,可以通过相关反射方法,改变其权限,创建多个实例。比如:

publicclassTest{publicstaticvoidmain(String args[]){privateSingleton(){};Singleton singleton=Singleton.getInstance();try{Constructor<Singleton> constructor=Singleton.class.getDeclaredConstructor();
            constructor.setAccessible(true);Singleton singletonnew= constructor.newInstance();System.out.println(singleton== singletonnew);//输出结果为 false}catch(Exception e){}}}

解决方案:可以给构造函数加上判断限制创建多个实例,如下:

privateSingleton(){if(null!=Singleton.singleton){thrownewRuntimeException();}}

五、单例模式中的单例对象会不会被垃圾回收

对于JDK1.2后的JVM HotSpot来说,判断对象可以回收需要经过可达性分析,由于单例对象被其类中的静态变量引用,所以JVM认为对象是可达的,不会被回收。
另外,对于JVM方法区回收,由堆中存在单例对象,所以单例类也不会被卸载,其静态变量引用也不会失效

六、多JVM/ClassLoader的系统使用单例类

不同ClassLoader加载同一个类,对类本身的对象(Singleton.class)来说是不一样的,所以可以创建出不同的单例对象,对不同JVM的情况更是如此,这些在JavaEE开发中还是比较常见。
所以,在多JVM/ClassLoader的系统使用单例类,需要注意单例对象的状态,最好使用无状态的单例类。

七、Spring(IOC框架)实现的单例

Spring的一个核心功能控制反转(IOC)或称依赖注入(DI):
高层模块通过接口编程,然后通过配置Spring的XML文件或注解来注入具体的实现类(Bean)。
这样的好处的很容易扩展,想要更换其他实现类时,只需要修改配置就可以了。通过IOC容器来实现,其默认生成的Bean是单例的(在整个应用中(一般只用一个IOC容器),只创建Bean的一个实例,多次注入同一具体类时都是注入同一个实例)

IOC容器来实现过程简述如下:
当需要注入Bean时,IOC容器首先解析配置找到具体类,然后判断其作用域(@Scope注解);
如果是默认的单例@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON),则查找容器中之前有没有为其创建了Bean实例;
如果有则直接注入该Bean实例,如果没有生成一个放到容器中保存(ConcurrentHashMap – map.put(bean_id, bean)),再注入。

注:其中解析配置查找具体类、生成Bean实例和注入过程都是通过Java反射机制实现的。

从上面可以了解到,Spring实现的单例和我们所说的单例设计模式不是一个概念:
前者是IOC容器通过Java反射机制实现,后者只是一种编程方法(套路)。
但总的来说,它们都可以实现“单例”。

参考如下:
https://blog.csdn.net/Ricky_Monarch/article/details/99407326
https://blog.csdn.net/tjiyu/article/details/76572617
https://blog.csdn.net/qq_36523667/article/details/79014324
https://blog.csdn.net/naerna/article/details/80498633
https://blog.csdn.net/zcw4237256/article/details/79670608

  • 作者:懒虫虫~
  • 原文链接:https://blog.csdn.net/jike11231/article/details/106229415
    更新时间:2022-08-10 12:07:12