广

Java编程

  • IOS开发
  • android开发
  • PHP编程
  • JavaScript
  • ASP.NET
  • ASP编程
  • JSP编程
  • Java编程
  • 易语言
  • Ruby编程
  • Perl编程
  • AJAX
  • 正则表达式
  • C语言
  • 编程开发

    review引发的有关于单例模式的思考

    2018-11-09 09:49:03 次阅读 稿源:互联网
    零七广告

    一次代码调试中发现一个情况,即我在查看memcached的connection时,发现总是维持在100来个左右,当然这看似没什么问题,因为memcached默认connection有1024个。但是我想的是为什么会有100来个,因为我的memcachedclient的产生采用的是单例模式我定义了一个memcachedClientFactory类,主要代码如下:

    代码如下:

    MemcachedClientFactory{
    private MemcachedConnectionBuilder memcachedConnectionBuilder;
    private String servers;
    private static MemcachedClient memcachedClient;

    private MemcachedClientFactory(){
    }

    private MemcachedClientFactory(MemcachedConnectionBuilder memcachedConnectionBuilder, String servers){
     this. memcachedConnectionBuilder= memcachedConnectionBuilder;
     this.servers=servers;
     }

    public static MemcachedClient createClient(){
    if(memcachedClient==null){
    this.memcahcedClien= new MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));
    }
     return this.memcahcedClient;
    }
    }
    }

    回到最初的问题,为什么会有100多个连接?

    上面这个写法真的能保证只产生一个连接?很显然是不能,为什么?多线程并发!问题就出在这里,当有多个线程同时进入createClient()方法时,而且刚好都判断为memcachedClient为null,这时候就产生了多个连接。哈,问题找到了。

    改进:

    代码如下:

    public static synchronizd MemcachedClient createClient(){
     if(memcachedClient==null){
    this.memcahcedClien=  new
    MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));
    }
     return this.memcahcedClient;
    }

    这样就ok了,改动很简单。程序是没有问题了,而且也能保证只有一个连接。

    不过抛开这个问题,我们可以继续就如何解决单例模式下的并发问题深入思考一下。

    我总结一下,要解决单例模式在并发下的问题,大概有三种方式:

    1. 不使用延迟实例化,而是用提前实例化。

    即程序改写为:

    代码如下:

    Public Class Singleton{
    private static Singleton instance=new Singleton();
    private Singleton(){};

    public static Singleton getInstance(){
       return instance;
    }
    }

    这样做时,jvm在加载类时就立马创建了该实例,所以这样做的前提是,创建该实例的负担不大,我不比过多的考虑性能,并且我们确认该实例是一定会用到的。其实我前面的代码也完全可以使用这个方式:

    代码如下:

    MemcachedClientFactory{
    private MemcachedConnectionBuilder memcachedConnectionBuilder;
    private String servers;
    private static MemcachedClient memcachedClien= new
    MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));

    private MemcachedClientFactory(){
    }

    private MemcachedClientFactory(MemcachedConnectionBuilder memcachedConnectionBuilder, String servers){
     this. memcachedConnectionBuilder= memcachedConnectionBuilder;
     this.servers=servers;
     }

    public static MemcachedClient createClient(){
     return this.memcahcedClient;
    }
    }
    }

    不过,看上去似乎没有问题,但是有隐患,即一旦有人不小心调用了memcachedClient.shutdown()方法,那整个程序就无法再生出新的memcachedClient了。当然这是极端情况了,但是为了代码的健壮,可以再改为:

    代码如下:

    public static MemcachedClient createClient(){
    if(memcachedClient==null){
    this.memcahcedClien= new MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));
    }
    return this.memcahcedClient;
    }

    2.  就是使用synchronized关键字。

    这么做可以保证同步问题,但是我们知道使用synchronized的开销是很大的,会严重影响性能,所以用这个的前提是,你确认不会经常调用这个方法,或者你创建这个instance的开销不会特别大。是否还可以改进,看 下面。

    3. 使用“双重检查加锁“,在getInstance中见识使用同步

    代码如下:

    public Class Singleton{
    private volatile static Singleton instance;

    private Singleton(){};
    public static Singleton getInstance(){
     if(instance==null){
      synchronized (Singleton.class){
     if(instance==null){
     instance=new Singleton();
    }
    }

    }
    return instance;
    }
    }

    零七网部分新闻及文章转载自互联网,供读者交流和学习,若有涉及作者版权等问题请及时与我们联系,以便更正、删除或按规定办理。感谢所有提供资讯的网站,欢迎各类媒体与零七网进行文章共享合作。

    零七广告
    零七广告
    零七广告
    零七广告