• 单例模式


    介绍

    类的单例设计模式,就是采取一定的方法来保证在整个软件系统中,某个类只存在一个对象实例。且该类只提供一个取得其对象实例的方法(静态方法)。

    八种方式

    单例模式有八种方式:

    1. 饿汉式(静态常量)
    2. 饿汉式(静态代码块)
    3. 懒汉式(线程不安全)
    4. 懒汉式(线程安全,同步方法)
    5. 懒汉式(线程安全,同步代码块)
    6. 双重检查
    7. 静态内部类
    8. 枚举

    步骤

    步骤大致如下:

    1. 构造器私有化(防止外部new)
    2. 类的内部创建对象
    3. 向外提供一个静态公共方法(getInstance)

    饿汉式(静态常量)

    代码:

    package singleton.type1;
    
    public class Singleton {
    
    	private Singleton() {
    		
    	}
    	
    	private static final Singleton instance = new Singleton();
    	
    	public static Singleton getInstance() {
    		return instance;
    	}
    	
    }
    

    测试代码:

    package singleton.type1;
    
    public class SingletonTest {
    
    	public static void main(String[] args) {
    		Singleton instance = Singleton.getInstance();
    		Singleton instance2 = Singleton.getInstance();
    		System.out.println(instance == instance2);
    		System.out.println(instance.hashCode());
    		System.out.println(instance2.hashCode());
    	}
    
    }
    

    测试结果:

    true
    2018699554
    2018699554
    

    说明

    1. 优点:写法简单,类装载时就完成了实例化。基于class loader机制避免了线程同步问题
    2. 缺点:类装载时就完成了实例化,没有懒加载的效果。如果从始至终并未使用该实例,则造成内存浪费

    结论:此模式可用,可能造成内存浪费

    饿汉式(静态代码块)

    代码:

    package singleton.type2;
    
    public class Singleton {
    
    	private Singleton() {
    		
    	}
    	
    	private static final Singleton instance;
    	
    	static {
    		instance = new Singleton();
    	}
    	
    	public static Singleton getInstance() {
    		return instance;
    	}
    	
    }
    

    测试代码和测试结果同上

    说明

    1. 这种方式和上面类似,只不过将类实例化的过程放在了静态代码块中。在类装载的时候,就执行静态代码块中的代码,初始化类的实例。优缺点同上。

    结论:此模式可用,可能造成内存浪费

    懒汉式(线程不安全)

    代码:

    package singleton.type3;
    
    public class Singleton {
    
    	private Singleton() {
    		
    	}
    	
    	private static Singleton instance;
    	
    	public static Singleton getInstance() {
    		if(null == instance) {
    			instance = new Singleton();
    		}
    		return instance;
    	}
    	
    }
    

    说明

    1. 优点:起到了懒加载效果,只在需要的时候才实例化对象
    2. 缺点:只能在单线程下使用。如果在多线程下,一个线程进入了if(null == instance)的判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时就会产生多个实例。所有在多线程环境下不可使用该方式。

    结论:实际开发中,不要用这种方式

    懒汉式(线程安全,同步方法)

    代码:

    package singleton.type4;
    
    public class Singleton {
    
    	private Singleton() {
    		
    	}
    	
    	private static Singleton instance;
    	
    	public static synchronized Singleton getInstance() {
    		if(null == instance) {
    			instance = new Singleton();
    		}
    		return instance;
    	}
    	
    }
    

    说明

    1. 优点:解决了线程安全问题
    2. 缺点:效率太低了。每个线程想获得类的实例对象时,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就够了,后面想获取该类实例,直接返回就行了。方法同步效率太低

    结论:实际开发中,不推荐使用这种方式

    懒汉式(线程安全,同步代码块)

    代码:

    package singleton.type5;
    
    public class Singleton {
    
    	private Singleton() {
    		
    	}
    	
    	private static Singleton instance;
    	
    	public static Singleton getInstance() {
    		if(null == instance) {
    			synchronized(Singleton.class) {				
    				instance = new Singleton();
    			}
    		}
    		return instance;
    	}
    	
    }
    

    说明

    1. 该方式本意是对第四张方式的改进,因为同步方法效率太低,改为同步产生实例的代码块
    2. 但是,这种方式并不能起到线程同步的作用。这里其实和第三种问题一样,加入一个线程进入了if(null == instance)语句块,还未执行下面的代码,另一个线程也通过了这个判断语句,还是会产生多个实例

    结论:实际开发中, 不能使用这种方式

    双重检查

    代码:

    package singleton.type6;
    
    public class Singleton {
    
    	private Singleton() {
    		
    	}
    	
    	private static volatile Singleton instance;
    	
    	public static Singleton getInstance() {
    		if(null == instance) {
    			synchronized(Singleton.class) {
    				if(null == instance) {					
    					instance = new Singleton();
    				}
    			}
    		}
    		return instance;
    	}
    	
    }
    

    这里需要额外说明一下,volatile关键字使得某个线程对某个对象(变量)的修改,其他线程能立刻感知到。

    说明

    1. Double-Check概念是多线程开发中经常出现的,如代码所示,我们进行了两次if(null == instance)检查,这样就可以保证线程安全了
    2. 第一次初始化时,即使是多线程,实例化代码也只执行一次。第一个线程进入同步块,实例化对象后,后面线程再次进入同步块时,判断if(null == instance),此时对象不为空,故第二次进入同步块实际什么也没做,之后返回实例化对象
    3. 之后再调用getInstance方法,在第一个if(null == instance)这里就跳过了同步块,直接返回实例化对象
    4. 综上,线程安全,延迟加载,效率较高

    结论:实际开发中,推荐使用这种单例设计模式

    静态内部类

    代码:

    package singleton.type7;
    
    public class Singleton {
    
    	private Singleton() {
    		
    	}
    	
    	private static class SingletonInstance {
    		private static final Singleton INSTANCE = new Singleton();
    	}
    	
    	public static Singleton getInstance() {
    		return SingletonInstance.INSTANCE;
    	}
    	
    }
    

    说明

    1. 静态内部类在Singleton类加载时不会立即实例化,在需要时才会实例化,这里就是调用getInstance()方法时,才会装载SingletonInstance类,从而完成Singleton的实例化。实现了懒加载
    2. 类的静态属性只会在第一次类加载的时候初始化,所以这里JVM帮我们保证了线程安全,在类初始化时,别的线程无法进入
    3. 综上,线程安全,利用静态内部类特点实现延迟加载,效率高

    结论:推荐使用

    枚举

    代码:

    package singleton.type8;
    
    public enum Singleton {
    
    	INSTANCE;
    	
    }
    

    说明

    1. 借助JDK1.5添加的枚举来实现单例模式
    2. 不仅能避免多线程问题,还能防止反序列化重新创建新的对象

    结论:推荐使用

    JDK实例

    在JDK中,java.lang.Runtime就是经典的单例模式(饿汉式)

    总结

    1. 单例模式保证了系统内存中该类只存在一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统性能
    2. 当要实例化一个单例类时,记得使用相应的方法获取对象,而不是使用new
    3. 单例模式使用场景:需要频繁进行创建和销毁对象、创建对象时耗时过多或耗费资源过多(即重量级对象),但又经常要用到的对象、工具类对象,频繁访问数据库或文件的对象(如数据源,session工厂等)
    4. 实际开发用类型:饿汉式(静态常量)、双重检查、静态内部类、枚举
  • 相关阅读:
    div嵌套出现的样式问题
    知乎最多支持答案的黄色渐隐效果
    对话框图形的css实现方式
    排序算法的相关问题
    数据库优化
    常用的Java基本代码汇总
    浅谈getStackTrace()方法(一)
    ECharts学习总结(三)-----基本概念分析
    ECharts学习总结(二)-----图表组件漏斗图(funnel)
    Echarts学习总结(一)-----柱状图
  • 原文地址:https://www.cnblogs.com/tenny-peng/p/11579144.html
Copyright © 2020-2023  润新知