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

发布时间:2021-10-25 15:58:14 作者:iii
来源:亿速云 阅读:322

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

程序代码如下:


   
   
  
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(".......");
   }
}
     
技术总监说:“这是个办法,不过,这样的实现方式有几个问题需要考虑。”

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

双重检测

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

   
   
  
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() 函数都不会进入到加锁逻辑中。

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

推荐阅读:
  1. 如何实现单例模式
  2. php 模式设计之单例模式

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

java

上一篇:怎么在Windows 10中禁用Cortana

下一篇:Python爬虫经常会被封的原因是什么

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》