单例模式之怎么实现我的机器人女友

本篇内容介绍了“单例模式之怎么实现我的机器人女友”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

成都创新互联公司专注于浦城企业网站建设,响应式网站建设,成都做商城网站。浦城网站建设公司,为浦城等地区提供建站服务。全流程按需求定制制作,专业设计,全程项目跟踪,成都创新互联公司专业和态度为您提供的服务

程序代码如下:


   
   
  
public class GirlFriend {

   private String name;

   public GirlFriend(String name) {
       this.name = name;
       System.out.println("机器人女友制作完成");
   }

   public void smile() {
       System.out.println("笑一个 :-)");
   }

   public void housework() {
       System.out.println("去干家务");
   }

   public void buKeMiaoShu() {
       System.out.println(".......");
   }
}
     
操作也很简单,直接创建对象就行了。

   
   
  
public static void main(String[] args) {
    GirlFriend girlFriend = new GirlFriend("小丽");
    girlFriend.smile();
    girlFriend.housework();
    girlFriend.buKeMiaoShu();
}
     
你看,马上就能干活了。

   
   
  
机器人女友制作完成
笑一个 :-)
去干家务
.......
     
小帅很快发现了一个漏洞,如果材料足够,他能创造无数个对象。
很快,小帅就在黑市上购买了一套材料,回家启动机器,果然制造出了第二个机器人女友。
小帅难掩心中的兴奋,盘算着,再去黑市买几套材料回来,不就能打印很多个“女友”了?

单例模式


创新基因公司的监控系统很快就发现了这个问题,工程师们加班加点在线升级了系统。
他们采用了一种叫做单例模式的设计模式来保证一台机器只能生成一个对象。

单例模式之怎么实现我的机器人女友


 更新后的代码如下:

   
   
  
public class GirlFriend {

   private static GirlFriend girlFriend;

   private String name;

   private GirlFriend(String name) {
       this.name = name;
       System.out.println("机器人女友制作完成");
   }

   /**
    * 对象通过getInstance方法获取
    * @param name
    * @return
    */
   public static GirlFriend getInstance(String name) {
       if(girlFriend == null) {
           girlFriend = new GirlFriend(name);
       }
       return girlFriend;
   }

   public void smile() {
       System.out.println("笑一个 :-)");
   }

   public void housework() {
       System.out.println("去干家务");
   }

   public void buKeMiaoShu() {
       System.out.println(".......");
   }
}
     

   
   
  
public static void main(String[] args) {
   GirlFriend girlFriend = GirlFriend.getInstance("小丽");
   girlFriend.smile();
   girlFriend.housework();
   girlFriend.buKeMiaoShu();
}
     
创新基因公司的工程师们很满意,他们开了一个盛大的party,庆祝工作的圆满完成。
小帅花了重金,在黑市上买了好几套材料,打算回家打造一个“后宫团”,想想以后的日子真是美滋滋啊。
小帅打开程序一看傻了眼,运行了好多次,获取的都是同一个对象,“后宫”梦就此破裂了吗?
小帅不甘心啊。
经过几天苦思冥想,小帅终于想到了破解方法,这就是多线程:

   
   
  
public static void main(String[] args){
   for(int i = 0; i < 5; i++) {
       new Thread(new Runnable() {
           @Override
           public void run() {
               GirlFriend girlFriend = GirlFriend.getInstance("小丽");
               System.out.println(girlFriend);
           }
       }).start();
   }
}
     
5个线程同时运行,顺利创建了3个不同的对象。

   
   
  
机器人女友制作完成
singleton.singleton.GirlFriend@95458f7
机器人女友制作完成
机器人女友制作完成
singleton.singleton.GirlFriend@d9d8ad0
singleton.singleton.GirlFriend@383a0ba
singleton.singleton.GirlFriend@d9d8ad0
singleton.singleton.GirlFriend@d9d8ad0
     
“呜。呜。呜。”,创新基因公司的报警器又响了起来,工程师们都一脸懵逼,这么完美的单例模式怎么还有破绽呢?
最后还是技术总监亲自出马,给工程师们讲解,顺便画了一幅图:

单例模式之怎么实现我的机器人女友


 线程1和线程2判断girlFriend的时候如果都为空,就会各自创建一个对象,最后就会返回两个不同的对象了。

工程师们恍然大悟。
“谁知道如何改进吗?”技术总监问道。

懒汉式

“这个简单,在getInstance方法上加个synchronized关键字就行了!”程序员老王得意的说。

   
   
  
/**
* 对象通过getInstance方法获取
 * @param name
 * @return
 */
public synchronized static GirlFriend getInstance(String name) {
    if(girlFriend == null) {
        girlFriend = new GirlFriend(name);
    }
    return girlFriend;
}
     
“这样确实可以,不过,”技术总监话锋一转,“你有没有考虑过效率问题?”
“synchronized同步方法只有第一次创建对象的时候能用到,也就是说一旦创建了girlFriend对象后就用不到这个同步功能了,但是以后每次调用getInstance方法都会进入同步代码,严重降低了效率。”
技术总监犀利地指出了问题所在。

饿汉式

“还有个办法,可以用全局变量,在类加载的时候就创建对象,所以,实例的创建过程是线程安全的。”程序员小李也想出了一个办法。

   
   
  
public class GirlFriend {

   // 在类加载的时候就创建对象,是线程安全的
   private static GirlFriend girlFriend = new GirlFriend("小丽");

   private String name;

   private GirlFriend(String name) {
       this.name = name;
       System.out.println("机器人女友制作完成");
   }

   /**
    * 对象通过getInstance方法获取
    * @return
    */
   public static GirlFriend getInstance() {
       return girlFriend;
   }

   public void smile() {
       System.out.println("笑一个 :-)");
   }

   public void housework() {
       System.out.println("去干家务");
   }

   public void buKeMiaoShu() {
       System.out.println(".......");
   }
}
     
技术总监说:“这是个办法,不过,这样的实现方式有几个问题需要考虑。”
  • 不支持延迟加载(在真正用到对象的时候,再创建实例),在类加载的时候对象就创建好了,如果对象在整个程序中一次都用不到,提前创建就浪费了。

  • 不能控制对象的数量,我们完全可以声明多个对象,比如:GirlFriend girlFriend1;GirlFriend girlFriend2;GirlFriend girlFriend3。

  • 我们可能没有足够的信息在静态初始化时,实例化每一个对象,对象的构造方法参数,可能要依赖程序后面的运算结果。


但是,我们要活学活用,如果创建对象比较耗时,等我们用到的时候再创建就会很慢,我们想在程序加载的时候提前创建好,是可以用这种方式的。  
“还有没有其他方法?”技术总监追问道。

双重检测

“还有一种办法,把同步锁放到方法里面,双重检测。”程序员老王想了好久,终于想出了另一种方法。

   
   
  
public class GirlFriend {

   // volatile关键字保证了每个线程看到的girlFriend对象都是最新的
   private volatile static GirlFriend girlFriend;

   private String name;

   private GirlFriend(String name) {
       this.name = name;
       System.out.println("机器人女友制作完成");
   }

   /**
    * 对象通过getInstance方法获取
    * @param name
    * @return
    */
   public static GirlFriend getInstance(String name) {
       if(girlFriend == null) {
           synchronized (GirlFriend.class) {
               if (girlFriend == null) {
                   girlFriend = new GirlFriend(name);
               }
           }
       }
       return girlFriend;
   }

   public void smile() {
       System.out.println("笑一个 :-)");
   }

   public void housework() {
       System.out.println("去干家务");
   }

   public void buKeMiaoShu() {
       System.out.println(".......");
   }
}
     
“检查girlFriend对象的时候,如果为null就进入同步代码,每个线程重新判断girlFriend对象是否为空,volatile关键字保证了每个线程看到的girlFriend对象都是最新的(在高版本的 Java中,这里已经不需要使用volatile了)。”
“如果girlFriend对象已经创建了,以后就不会进入同步代码了,这样就保证了效率。”老王解释道。
“恩,这是个好方法,这样就解决懒汉式方法的低性能和饿汉式方法的延迟加载问题,我们就采用这个方案升级代码吧。”技术总监赞许道。

总结

单例模式(Singleton Pattern):单例模式确保一个类仅有一个实例,并提供一个访问它的全局访问点。
单例模式的有三个要点:
  • 某个类只能有一个实例

  • 它必须自行创建这个实例

  • 它必须自行向整个系统提供这个实例

单例模式是一种对象创建型模式。
单例模式又名单件模式或单态模式。
单例的实现单例有下面几种经典的实现方式:
- 懒汉式
懒汉式相对于饿汉式的优势是支持延迟加载。但是,这种实现方式会导致频繁加锁、释放锁,以及并发度低等问题,频繁的调用会产生性能瓶颈。
- 饿汉式
饿汉式的实现方式,在类加载的期间,就已经将静态实例初始化好了,所以,实例的创建是线程安全的。不过,这样的实现方式不支持延迟加载实例。
- 双重检测
双重检测的实现方式是既支持延迟加载、又支持高并发的单例实现方式。只要实例被创建之后,再调用 getInstance() 函数都不会进入到加锁逻辑中。

“单例模式之怎么实现我的机器人女友”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注创新互联网站,小编将为大家输出更多高质量的实用文章!


名称栏目:单例模式之怎么实现我的机器人女友
文章出自:http://myzitong.com/article/ijhoge.html