单例模式
- 单例模式的核心作用
保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。 - 常见应用场景
Windows的任务管理器和回收站,读取配置文件的类一般也只有一个对象,网站的计数器,应用程序日志应用实现,数据库连接池的设计,操作系统的文件系统,Servlet中的Application,Spring中每个Bean默认是单例,Servlet编程中每个Servlet也是单例,SpringMVC和struts1中控制器对象也是单例。 - 单例的优点
当一个对象的产生需要比较多的资源时,可以通过在应用启动时直接产生一个单例对象,然后通过永久驻留内存的方式解决;可以在系统设置全局的访问点,优化共享资源访问,如负责所有数据表的映射处理。 - 常见五种实现形式:
- 饿汉式(线程安全,调用效率高,不能延时加载)
public class A{
private static A a = new A();//类初始化时,立即加载这个对象
private A(){}
public static A getInstance(){return a;}
}
饿汉式单例中,static变量会在类装载时初始化,也不会涉及多个线程对象访问该对象的问题,虚拟机保证只会装载一次该类,不会发生并发访问问题。可以省略synchronized。但是如果只是加载本类,而不是要调用getInstance方法,会造成资源浪费!
- 懒汉式(线程安全,效率不高,可以延时加载
public class A{
private static A a;
private A(){}
public static synchronized A getInstance(){
if(a==null){a = new A();}
return a;
}
}
lazyload:延时加载,懒加载,真正用的时候加载。每次调用getInstance方法都要同步,并发效率低。
- 双重检测锁式(由于JVM底层内部模型原因,偶尔会出问题
这个模块将同步内容放到了if内部,提高了执行效率,不必每次获取对象时都进行同步,只有第一次才同步。 - 静态内部模式(线程安全,效率高,可以延时加载
静态内部类实现方式(也是一种懒加载方式):
public class A{
private static class AInstance{
private static final A a = new A();
}
public static A getInstance(){return AInstance.a;}
private A(){}
}
外部类没有static属性,不会立即加载对象。只有真正调用getInstance方法才会加载匿名内部类,兼备了并发高效调用和延时加载。
- 枚举单例(线程安全,效率高,不能延时加载
public enum A{INSTANCE;//一个枚举元素,它就代表了Singleton的一个实例
public void AOperation(){//功能处理}
}//单例可以有自己的操作
优点:实现简单,枚举本身就是单例模式(由JVM从根本上提供保障,避免反射和反序列化的漏洞)。
缺点:无延时加载。
- 单例模式存在的问题
- 防止反射:在构造方法中手动抛出异常控制
private A(){if(a!=null){throw new RuntimeException();}}
- 防止反序列化:在单例类中建一个新方法
private Object readResolve() throws ObjectStreamException{return a;}
反序列化时,定义了此方法则直接返回此方法制定的对象,而不会单独创建新对象。
- 多线程环境下效率由高到低:
饿汉式 > 静态内部类式 > 枚举式 > 双重检查锁式 > 懒汉式
选用:单例对象占用资源少,不需要延时加载:枚举式>饿汉式。
单例对象占用资源大,需要延时加载:静态内部类式>懒汉式。
网友评论