设计模式-单例模式
单例模式, 就是采取一定方法保证在整个软件系统中, 对某个类只能存在一个对象实例, 并且该类只提供一个取得其对象实例的方法(静态方法)
使用场景
- 需要频繁进行创建和销毁的对象
- 创建对象耗时过多或耗费资源过多(重量级对象), 但又常用到的对象
- 工具类对象
- 频繁访问数据库或文件的对象(DataSource, session工厂)
八种方式
饿汉式(静态常量)
- 构造器私有化
- 类的内部创建对象
- 对外暴露一个静态的公共方法
1 | class Singleton { |
优缺点:
- 优点: 写法比较简单, 类装载时候就完成实例化. 避免了线程同步问题
- 缺点: 在类装载的时候就完成实例化,没有达到Lazy Loading的效果. 但是, 导致类装载的原因有很多种. 如果从始至终没有用到这个实例, 将会造成==内存浪费==
饿汉式(静态代码块)
- 与上面相似, 只不过将类实例化放在了静态代码块.
1 | class Singleton { |
优缺点:
与上面类似, 通过jvm加载机制可以知道, static代码块和static常量的初始化在jvm编译器优化后, 其实是合并在一起的(等价), 执行先后顺序由代码的先后顺序评定
注意: 因为clinit方法是线程安全, 所以加载类的时候如果static加载的东西太多也会很慢
==内存浪费==
final的作用: 加final修饰的对象, 在连接-准备阶段就已经赋值, 没有final的在初始化阶段赋值
懒汉式(线程不安全)
1 | class Singleton { |
优缺点:
- 起到了Lazy Loading的效果, 但是只能在单线程下使用
- 多线程下, 一个线程进入了
if (instance == null)
语句块后, 还没执行后, 另一个线程也进入了这个语句块. 就会产生多个实例.
==实际开发不要使用这种方式==
懒汉式(线程安全, 同步方法)
1 | class Singleton { |
优缺点:
- 解决了上述的线程不安全的问题
- 效率低. 因为只有在第一次才会初始化instance, 其他的时间getInstance方法只是为了获取而已, 这样加入synchronized将会降低很多效率
==实际开发中不推荐==
懒汉式(线程不安全,同步代码块)
1 | class Singleton { |
优缺点:
- 不能起到线程同步的作用: 这样写也没有用! 和上面是一样的, 因为只要if判断结束进入if块中, 线程排队执行同步代码块, 第一个执行完了后, 第二个仍旧要执行! 连线程安全都做不到
==实际开发中不能使用==
双重检查
1 | class Singleton { |
优缺点:
- Double-Check概念其实是在多线程开发中经常用到的.
- 实例化代码只执行了一次
- 线程安全; 延迟加载; 效率较高
==开发中推荐使用==
静态内部类
静态内部类的特点就是:
- 当外部类装载的时候, 内部类是不会装载的.
- 当getInstance的时候只会装载一次, 还能保证线程安全
jvm在装载类的时候是线程安全的
1 | class Singleton { |
优缺点:
- 采用了类装载的机制来保证初始化实例时只有一个线程. jvm帮助我们保证了线程的安全性, 在类初始化时, 别的线程无法进入
==推荐使用==
枚举
1 | enum Singleton{ |
优缺点:
- jdk1.5+后实现的枚举
- 避免多线程同步问题
- 防止反序列化重新创建新的对象
- 其他方法都可以通过反射破坏, 只有枚举不可以
==强烈推荐==
总结
推荐使用:
- 饿汉式(静态常量)
- 饿汉式(静态代码块)
- 懒汉式(线程不安全)
- 懒汉式(线程安全,同步方法)
- 懒汉式(线程不安全,同步代码块)
- 双重检查
- 静态内部类
- 枚举
所以懒汉式团灭 = =
jdk中单例模式的应用: Runtime类, 使用了饿汉式