五种方式构建单例模式:懒汉式,饿汉式,双重检测锁(不推荐),静态内部类(推荐),枚举单类(推荐)
单例模式就是保证一个类只有一个实例(对象),并且提供一个访问该实例的全局访问点。
当一个对象的产生需要比较多资源时,如读取配置、产生其他依赖对象时,则可以在应用启动时直接产生一个单例对象,然后驻留内存的方式来解决。
由于单例模式只生成一个实例,减少了系统开销。单例模式可以设置全局访问点,优化共享资源访问,如可以设计一个单例类,负责所有数据表的映射处理。
常见5种单例模式实现方式:
1 饿汉式(线程安全,调用效率高,不能延时加载)
1 | public class Singleton { |
2 懒汉式(线程安全,调用效率不高,可以延时加载)
1 | public class Singleton /*implements Serializable*/ { |
使用反射方式直接调用私有构造器:
1 | Class<Singleton> class = (Class<Singleton>) class.forName("com.xinxing.Singleton"); |
通过反序列化的方式构造多个对象:
1 | Singleton s1 = Singleton.getInstance(); |
3 静态内部类式(线程安全,调用效率高,可以延时加载)(推荐使用)
类在被加载时是线程安全的,也是一种懒加载方式,很多框架都用这种框架。
- 外部类没有static属性,则不会像饿汉模式那样立即加载对象。
- 只有真正调用getInstanc()时,才会加载静态内部类,加载类时是线程安全的,instance是static final类型,保证了内存中只有这样一个实例存在,而且只能被赋值一次。
- 兼备了线程安全、并发高效、延迟加载的优势
1 | public class Singleton { |
4 枚举单例(线程安全,调用效率高,不能延时加载)(建议使用)
优点:有句JVM从根本上提供保障,避免和反射和反序列化调用私有方法的漏洞,简单高效
缺点:没有延时加载的效果
1 | public enum Singleton { |
5 双重检测锁式(不建议使用)
由于懒汉模式是将方法放到同步块里,执行时整个方法都被锁定效率比较低,该模式将同步内容放到if内部,提高了执行效率,不必每次获取对象时都进行同步,只有第一次才同步创建以后就不会同步了。
问题:由于编译器优化原因和JVM底层内部模型原因,偶尔会出现问题,不建议使用
五种单例模式在多线程环境下的效率
饿汉式 | 22ms |
---|---|
懒汉式 | 636ms |
静态内部类式 | 28ms |
枚举式 | 32ms |
双重检查锁式 | 65ms |
典型的应用场景:
- windows的任务管理器
- window的回收站
- 项目中读取配置文件的类
- 网站计数器
- 应用程序的日志应用
- 数据库连接池
- 操作系统的文件系统
- Application(Servlet Context)
- Spring 中每个bean默认就是单例
- Servlet编程中,每个Servlet也是单例
- SpringMVC/struts中控制对象