单例模式基本定义:程序运行时,在java虚拟机中只存在该类的一个实例对象。
注意:
- 1、单例类只能有一个实例。
- 2、单例类必须自己创建自己的唯一实例。
- 3、单例类必须给所有其他对象提供这一实例。
目录
饿汉式
懒汉式(线程不安全,多线程不推荐使用)
懒汉式(线程安全,同步方法,也不推荐用,效率底)
双重检测(推荐使用)
静态内部类方式(推荐使用)
枚举(推荐使用)
饿汉式
懒汉式(线程不安全,多线程不推荐使用)
- 构造器私有化(不可被 new)
- 类的内部创建对象(类初始化时立刻实例化,可能会浪费内存)
- 对外部暴露一个静态的公共方法 getInstance
/** * 饿汉式单例模式 */ public class ClassA { //1.私有化构造方法,外部不能new,限制产生多个对象 private ClassA(){ } //2.在类的内部创建一个类的实例 //类初始化时,立即实例化这个对象(没有延时加载的优势)。加载类时,天然的是线程安全的! private static final ClassA instance = new ClassA(); //3.对外部提供调用方法:将创建的对象返回,只能通过类来调用 //方法没有同步,调用效率高! public static ClassA getInstance(){ return instance; } //测试 public static void main(String[] args) { ClassA a = ClassA.getInstance(); ClassA b = ClassA.getInstance(); System.out.println(a==b); } }
优点:
这种方法在类加载的时候就进行实例化,避免线程同步问题;
缺点:
在类加载的时候就完成实例化,没有达到Lazy Loading(懒加载)的效果,如果从开始都结束从未使用该实例,则会导致线程浪费
懒汉式(线程安全,同步方法,也不推荐用,效率底)用到的时候才实例化
/** * 懒汉式单例模式 */ public class ClassB { //1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象 private ClassB(){ } //2.在类的内部创建一个类的实例 private static ClassB instance ; //3.对外部提供调用方法:将创建的对象返回,只能通过类来调用 public static ClassB getInstance(){ if(instance == null) { instance = new ClassB(); } return instance; } //测试 public static void main(String[] args) { ClassB a = ClassB.getInstance(); ClassB b = ClassB.getInstance(); System.out.println(a==b); } }
特点:
起到Lazy Loading效果,但是只能在单线程下使用。
如果在多线程下,一个线程在执行 if(instance == null) 的时候还未来得及往下执行,另一个线程也通过这调判断语句,这样就会产生多个实例,所以在多线程下不可使用。
结论:开发中 不要 使用
双重检测(推荐使用)添加 synchronized
/** * 懒汉式单例模式 */ public class ClassB { //1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象 private ClassB(){ } //2.在类的内部创建一个类的实例 private static ClassB instance ; //3.对外部提供调用方法:将创建的对象返回,只能通过类来调用 public static synchronized ClassB getInstance(){ if(instance == null) { instance = new ClassB(); } return instance; } //测试 public static void main(String[] args) { ClassB a = ClassB.getInstance(); ClassB b = ClassB.getInstance(); System.out.println(a==b); } }
特点:
解决线程不安全问题
效率低,每个线程在获取类的实例时,执行getInstance()方法都要进行同步,而其实可以只执行一次实例化代码,后面需要获得该类实例直接return就行,所以该方法效率低
结论:
结论:开发中 不推荐 使用
静态内部类方式(推荐使用)/** * 使用双重校验锁来实现单例模式 */ public class ClassE { //1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象 private ClassE(){ } //2.在类的内部创建一个类的实例 //volatile作用:解决命令重排问题 private volatile static ClassE instance; //3.对外部提供调用方法:将创建的对象返回,只能通过类来调用 public static ClassE getInstance(){ if(instance == null){ //检查实例,如果为空,就进入同步代码块 synchronized (ClassE.class){ if(instance == null){ //再检查一次,仍未空才创建实例 instance = new ClassE(); } } } return instance; } //测试 public static void main(String[] args) { ClassE a = ClassE.getInstance(); ClassE b = ClassE.getInstance(); System.out.println(a==b); } }
线程安全,加volatile的作用是禁止指令重排。
枚举(推荐使用)有点类似饿汉式,但又能做到了延迟加载
/** * 使用静态内部类方式实现单例模式 */ public class ClassC { //1.私有化构造方法,使得在类的外部不能调用此方法,限制产生多个对象 private ClassC(){ } //2.在类的内部创建一个类的实例 private static class Holder{ private static ClassC instance = new ClassC(); } //3.对外部提供调用方法:将创建的对象返回,只能通过类来调用 public static ClassC getInstance(){ return Holder.instance; } //测试 public static void main(String[] args) { ClassC a = ClassC.getInstance(); ClassC b = ClassC.getInstance(); System.out.println(a==b); } }
静态内部类(Holder)在外部类(ClassC)被装载的时候不会被实例化,当调用getInstance时,才会装载Holder,从而完成ClassC才会被实例化,且创建过程的线程安全性,由 JVM 来保证
线程安全,利用静态内部类实现延迟加载,效率高
1
2
/** * 使用枚举方法实现单例模式 */ public enum ClassD { //定义一个枚举的元素,它就代表了Singleton的一个实例。 INSTANCE; //对外部提供调用方法:将创建的对象返回,只能通过类来调用 public void otherMethod(){ //功能处理 } } //测试 public static void main(String[] args) { ClassD a = ClassD.INSTANCE; ClassD b = ClassD.INSTANCE; System.out.println(a==b); }
线程安全,实现简单,调用效率高,不能延时加载。
枚举本身就是单例模式,由JVM从根本上提供保障并且可以天然的防止反射和反序列化漏洞!需要继承的场景它就不适用了。枚举方式是Effective Java作者提倡的方式。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)