经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
大话设计模式笔记(十八)の单例模式
来源:cnblogs  作者:callmeDevil  时间:2019/8/19 8:52:31  对本文有异议

单例模式

定义

保证一个类仅有一个实例,并提供一个访问它的全局访问点。

通常我们可以让一个全局变量使得一个对象被访问,但它不能防止你实例化多个对象。一个最好的办法就是,让类自身负责保存它的唯一实例。这个类可以保证没有其他实例可以创建,并且它可以提供一个访问该实例的方法。

UML图

方式一:单线程下的单例

  1. /**
  2. * Created by callmeDevil on 2019/8/17.
  3. */
  4. public class Singleton {
  5. private static Singleton instance;
  6. private Singleton(){} //构造方法私有,防止外界创建实例
  7. // 获得本类实例的唯一全局访问点
  8. public static Singleton getInstance(){
  9. if (instance == null) {
  10. //若实例不存在,则创建一个新实例,否则直接返回已有实例
  11. instance = new Singleton();
  12. }
  13. return instance;
  14. }
  15. }

测试

  1. public class Test {
  2. public static void main(String[] args) {
  3. Singleton s1 = Singleton.getInstance();
  4. Singleton s2 = Singleton.getInstance();
  5. if (s1 == s2) {
  6. System.out.println("两个对象是相同的实例");
  7. }
  8. }
  9. }

测试结果

  1. 两个对象是相同的实例

在没有并发问题的情况下,这种方式也是使用比较多的。但缺点也很明显,多线程下根本没法用。

方式二:多线程下的单例

  1. /**
  2. * Created by callmeDevil on 2019/8/17.
  3. */
  4. public class SingletonOnLock {
  5. private static SingletonOnLock instance;
  6. private SingletonOnLock(){}
  7. public static SingletonOnLock getInstance(){
  8. // 同步代码块,只有一个线程能进入,其他阻塞
  9. synchronized (SingletonOnLock.class){
  10. if(instance == null){
  11. instance = new SingletonOnLock();
  12. }
  13. }
  14. return instance;
  15. }
  16. }

存在问题

当存在对象实例时,完全不用担心并发时导致中创建多个实例,但每次调用 getInstance() 方法时都被加锁,是会影响性能的,因此这个类可以继续改良。

方式三:双重锁定(DCL)

  1. /**
  2. * Created by callmeDevil on 2019/8/17.
  3. */
  4. public class SingletonOnDoubleCheckLock {
  5. private static SingletonOnDoubleCheckLock instance;
  6. private SingletonOnDoubleCheckLock(){}
  7. public static SingletonOnDoubleCheckLock getInstance(){
  8. // 先判断实例是否存在,不存在再考虑并发问题
  9. if (instance == null) {
  10. synchronized (SingletonOnDoubleCheckLock.class){
  11. if(instance == null){
  12. instance = new SingletonOnDoubleCheckLock();
  13. }
  14. }
  15. }
  16. return instance;
  17. }
  18. }

两次判断实例是否存在的原因

当实例存在时,就直接返回,这是没有问题的。当实例为空并且有两个线程调用 getInstance() 方法时,它们都可以通过第一重 instace == null 的判断,然后由于 synchronized 机制,只有一个线程可以进入,另一个阻塞,必须要在同步代码块中的线程出来后,另一个线程才会进入。而此时如果没有第二重的判断,那第二个线程仍然会创建实例,这就达不到单例的目的了。

但这种方式是最让人“诟病”的一种不推荐方式,技巧看上去很好,但实际上同样影响性能

方式四:静态初始化

  1. /**
  2. * 该类声明为final ,阻止派生,因为派生可能会增加实例
  3. * Created by callmeDevil on 2019/8/17.
  4. */
  5. public final class SingletonStatic {
  6. // 第一次引用类的任何成员时就创建好实例,同时没有并发问题
  7. private static final SingletonStatic instance = new SingletonStatic();
  8. private SingletonStatic(){}
  9. public static SingletonStatic getInstance(){
  10. return instance;
  11. }
  12. }

JVM第一次加载类的时候就已经创建好了实例,如果接下来的很长时间都没有用到的话,占用的内存相当于被浪费了,也不是最让人推荐的一种方式。当然现在的服务器容量也越来越大,单单一个实例的内存也并不是任何情况都要考虑节省。除非追求极致。。

总结

  • 静态初始化的方式是自己被加载时就已经将自己实例化,因此也被称为“饿汉式”。
  • 其他方式是要在第一次被引用时,才会将自己实例化,所以被称为“懒汉式”。
  • 其实单例模式还有很多种实现方式,下面再提一种《大话设计模式》中未提到的实现:静态内部类。 这是楼主在《Java并发编程实战》中看到的,也是最为推荐的一种。

扩展

方式五:静态内部类

  1. /**
  2. * Created by callmeDevil on 2019/8/17.
  3. */
  4. public class SingletonStaticClass {
  5. private SingletonStaticClass() {}
  6. public SingletonStaticClass getInstande() {
  7. return InterClass.instance;
  8. }
  9. // 静态内部类,没有并发问题
  10. private static final class InterClass {
  11. public static SingletonStaticClass instance = new SingletonStaticClass();
  12. }
  13. }

推荐原因

JVM第一次加载外部的 SingletonStaticClass 时,并不会直接实例化,所以这种方式也属于“懒汉式”。只有在第一次调用 getInstance() 方法时,JVM才会加载内部类 InterClass,接着才实例化静态变量,也就是我们需要的外部类的单例。这样不仅延时了实例化,同时也解决了并发访问的问题,因此该方式是最为推荐的一种方式。

原文链接:http://www.cnblogs.com/call-me-devil/p/11368991.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号