常见的场景
(1)比如说,你自定义了一个框架,自定义了一份xml格式的一个配置文件,你要读取这个配置文件,这个配置文件中的数据,读取到类中,这个类的实例,只要保存一份就可以。那么此时可以使用单例模式,将这个类做成他的实例只能有一个,在这个实例中保存了配置文件中的数据 (2)类似于我们之前给大家讲解的那个工厂模式,有些工厂是需要实例化对象的,要基于实例化对象,来实现继承、接口、实现等功能,工厂实例可以做成单例的,就可以了 (3)你自己去判断,如果是一个类的实例只需要保持一份,那就做成单例
1.饿汉模式
package com.example.demo.pattern.singleton;/*** 饿汉模式* @author chenchao**/public class HungrySingletonPatternDemo {public static void main(String[] args) {Singleton singleton = Singleton.getInstance();singleton.execute();new Singleton();}public static class Singleton {/*** 第一步:直接就是将这个类的实例在创建出来,赋予static final修饰的变量** static:就是一个类的静态变量* final:这个变量的引用第一次初始化赋予之后,就再也不能修改引用了**/private static final Singleton instance = new Singleton();/*** 第二步:将构造函数搞成private私有的** 此时除了这个类自己本身,其他任何人都不能创建它的这个实例对象**/private Singleton() {}/*** 第三步:给一个static静态方法,返回自己唯一的内部创建的一个实例* @return*/public static Singleton getInstance() {return instance;}public void execute() {System.out.println("单例类的方法");}}}
2.常用内部内
package com.example.demo.pattern.singleton;/*** 这个才是我们实际开发过程中,最最常用的单例模式,内部类的方式来实现** @author chenchao**/public class InnerClassFullSingletonPatternDemo {/*** 可以做饱汉模式** 内部类,只要没有被使用,就不会初始化,Singleton的实例就不会创建** 在第一次有人调用getInstance方法的时候,内部类会初始化,创建一个Singleton的实例** 然后java能确保的一点是,类静态初始化的过程一定只会执行一次** @author chenchao**/public static class Singleton {private Singleton() {}public static class InnerHolder {public static final Singleton instance = new Singleton();}public static Singleton getInstance() {return InnerHolder.instance;}}}
3.饱汉模式 线程不安全
package com.example.demo.pattern.singleton;/*** 线程不安全的饱汉模式* @author chenchao**/public class UnsafeFullSingletonPatternDemo {/*** 线程不安全** @author chenchao**/public static class Singleton {private static Singleton instance;private Singleton() {}public static Singleton getInstance() {/** 假设有两个线程过来** 线程的基础:线程是并发着执行的,cpu,先执行一会儿线程1,然后停止执行线程1;切换过去执行线程2* 执行线程2一会儿之后,再停止执行线程2;回来继续执行线程1** 第一个线程,判断发现说instance == null,代码就进入到了下面去* 第二个线程,执行到这儿,发现,此时instance == null,那么就没什么问题了,继续往下走**/if(instance == null) {// 第一个线程跑到了这儿来,但是此时第一个线程,还没有执行下面的那行代码// 此时,第二个线程代码也执行到了这儿,cpu切换回线程1// 执行线程1的代码,线程1会创建一个实例出来// 但是切换到线程2去执行的时候,线程2,的代码已经执行到这儿来了,此时又会再一次执行下面的代码// 就是会再一次创建一个实例,之前线程1创建的那个实例,就会被垃圾回收,废弃掉了instance = new Singleton();}return instance;}}}
4. 线程安全 double check Lock DCL
虽然上了DCL双重检查但是还是有一定几率线程不安全,推荐用第二种内部内方式,基于JDK线程安全。
package com.example.demo.pattern.singleton;/*** 饿汉模式* @author chenchao**/public class SafeFullSingletonPatternDemo {public static class Singleton {private static Singleton instance;private Singleton() {}// 不是完美的// 因为不同的JVM的编译器的问题,可能导致说,这个情况下,还是线程不安全的// 具体的我不再这儿讲,因为涉及到复杂的JVM内部的原理public static Singleton getInstance() {// 如果线程1和线程2都执行到了这一步,然后此时线程1判断发现还是null// 线程2此时判断发现instance == null,也会进去if(instance == null) {// 线程1就会进来,此时线程1停止,切换到线程2// 线程2也会进来,此时切换到线程1// 线程1,发现这里需要加锁, 在这里加锁,获取到了这个锁// 线程2过来,线程2发现说,我也想要在这里加锁,发现说这个锁被人加了,线程2挂起等待别人释放锁// 此时切换回线程2,线程2发现锁被释放,然后在这里加锁synchronized(SafeFullSingletonPatternDemo.class) {// 线程1就进来了,此时切换到线程2// 切换回线程1,线程1此时在这里,再次判断,instance == null// 线程2就进来了,double check,如果这里没有instance == null的判断,那么线程2就会再次创建// 一个实例// 但是这里是双重检查,线程2又判断了一下,instance == null?否,不是nullif(instance == null) {// 线程1就会进来,创建一个实例instance = new Singleton();}}}// 这边出来以后,线程1就释放锁了// 线程2跳出来,直接获取一个instance返回了,这个instance就是之前线程1创建的实例return instance;}}}
5.说明
最简单的一种类了,就是如果一个类就只需要一次,那么就使用这个类了
但是使用单例模式有一个要求,不允许这个类的逻辑过于复杂,一般就是持有某份配置文件的配置,或者是别的一些数据
