欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2023-11-27 10:01:52
一次代码调试中发现一个情况,即我在查看memcached的connection时,发现总是维持在100来个左右,当然这看似没什么问题,因为memcached默认connec...

一次代码调试中发现一个情况,即我在查看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;
}
}