`
gong1208
  • 浏览: 558933 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

一次代码review引发的关于单例模式的思考

阅读更多

              一次代码review引发的关于单例模式的思

一次代码调优中发现一个情况,即我在查看memcached的connection时,发现总是维持在100来个左右,当然这看似没什么问题,因为memcached默认connection有1024个。但是我想的是为什么会有100来个,因为我的memcachedclient的产生采用的是单例模式,so let’s get into the code:

我定义了一个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;
}
}

 这样减少了synchronized出现的次数。当然使用volatile关键字有局限性,就是你的jdk必须是1.5以后的版本。

6
4
分享到:
评论
16 楼 gong1208 2013-04-02  
我只是简单写了下,没想到大家这么热心,给了这么多建议,而且很多建议很好,想的比我深入,十分感谢啊
15 楼 JasonWo 2013-04-02  
获取实例判断使用同步双重锁可将漏洞降到最低,但比较耗资源.
14 楼 jiakechong 2013-04-02  
風一樣的男子 写道
我习惯用静态内部类
public class Singleton{  
      private static class Holder {
           private static final Singleton INSTANCE = new Singleton();
      }
      private Singleton(){};  
  
      public static Singleton getInstance(){  
          return Holder.INSTANCE ;  
      }  
} 


+1
13 楼 greemranqq 2013-04-02  
这样不行,如果两个线程 同时执行了 createClient方法,如果memcachedClient 都是null,那么 会拿到两个 对象,就失去效果了。

还得对 直接调 获得对象的枷加锁方法
12 楼 xiaoyu1985ban 2013-04-02  
建议LZ考虑使用枚举来实现单例模式,具体可以参考《Effective Java》。
11 楼 gnomewarlock 2013-04-02  
3楼的才是正解,review代码必须从根上解决问题。
只有静态内部类才能从ClassLoader机制上避免多次实例化,当然前提是ClassLoader必须是单一的
10 楼 lzxz1234 2013-04-02  
内部类加持基本算是最好的,双重检查应该放弃
9 楼 LewisHe 2013-04-01  
don't act smart. Be simple.

The simple approach you mention will work in most case.
Synchronization never really necessary.
8 楼 learnworld 2013-04-01  
双重检查可能会失效,http://www.ibm.com/developerworks/cn/java/j-dcl.html
7 楼 ricoyu 2013-04-01  
经典的单列模式的三种实现方式,书上都有
6 楼 shadowlin 2013-04-01  
之前看过一篇文章。不过表老了,说双重锁机制(DCL)也不保险的
5 楼 fighting_2013 2013-04-01  
楼主说的其实就是单例模式的双重检查锁定吧
4 楼 jiushiyouke 2013-04-01  
semmy 写道
应该锁变量,也可以提高性能,静态方法锁,是锁住整个类级别的
public static MemcachedClient createClient(){    
    if(memcachedClient==null){    
        syncInit();  
    }    
    return this.memcahcedClient;    
}   
  
public static void syncInit(){
    synchronizd(memcachedClient){
    if(memcachedClient==null){    
        this.memcahcedClien=  new    
MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));  
    }  
    }
}  


你确定你这样写可以运行? 你能对null加锁么?
3 楼 風一樣的男子 2013-04-01  
我习惯用静态内部类
public class Singleton{  
      private static class Holder {
           private static final Singleton INSTANCE = new Singleton();
      }
      private Singleton(){};  
  
      public static Singleton getInstance(){  
          return Holder.INSTANCE ;  
      }  
} 
2 楼 semmy 2013-04-01  
应该锁变量,也可以提高性能,静态方法锁,是锁住整个类级别的
public static MemcachedClient createClient(){    
    if(memcachedClient==null){    
        syncInit();  
    }    
    return this.memcahcedClient;    
}   
  
public static void syncInit(){
    synchronizd(memcachedClient){
    if(memcachedClient==null){    
        this.memcahcedClien=  new    
MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));  
    }  
    }
}  
1 楼 wwwcomy 2013-04-01  
public static synchronizd MemcachedClient createClient(){  
    if(memcachedClient==null){  
this.memcahcedClien=  new  
MemcachedClient(memcachedConnectionBuilder.build(),AddrUtil.get(servers));  
}  
    return this.memcahcedClient;  
} 


这样直接加锁不太好吧  应该判断为空的时候 调用里面的加锁方法:

public static MemcachedClient createClient(){  
    if(memcachedClient==null){  
        syncInit();
    }  
    return this.memcahcedClient;  
} 

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


这样不用每次拿实例都经过锁了 只是第一次的时候要经过锁。

相关推荐

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

    单例模式的并发问题通常出现在延迟初始化(Lazy Initialization)的情况下,即只有在第一次调用`getInstance()`时才创建实例。为了解决这个问题,作者将`createClient()`方法改为同步的(synchronized),确保同一...

    Gitlab 代码 review 插件

    Gitlab 代码 review 插件是开发者们在进行代码审核时的一个强大工具,它专为内网环境下的 Gitlab 平台设计。该插件旨在优化源码浏览体验,提高团队协作效率,确保代码质量。在本文中,我们将深入探讨 Gitlab 代码 ...

    Coding Review Checklist(代码审查清单).pdf

    本文将根据“Coding Review Checklist(代码审查清单)”的内容,详细解析其涉及的关键知识点。 #### 二、常规项 1. **代码运行性**:确认代码能够正常运行,这是最基本的要求。如果代码无法运行,那么后续的所有...

    代码Review 文档

    1. **定期进行Code Review**:确保每周至少有一次40分钟的Code Review会议,这有助于保持团队对代码质量的关注度。 2. **合理分组**:小组人数不超过10人,确保至少有一名经验丰富的成员担任组长,他应具备模块设计...

    代码review指南

    代码审查(Code Review)是软件开发过程中的重要环节,它涉及到多个角色和一系列的步骤,目的是在开发初期就发现代码中的错误,提高代码质量,促进团队成员之间的知识共享和技能提升。在这份代码审查指南中,将详细...

    ReviewBoard + Tao-ReviewBoard + SVN 搭建代码审阅平台

    首先,ReviewBoard是一款开源的代码审查工具,它允许开发者提交代码供其他团队成员审核,提供了一个方便的界面来讨论代码变更。Tao-ReviewBoard是Eclipse的一个插件,用于与ReviewBoard集成,使得在Eclipse中可以...

    使用reviewboard和svn进行代码审查配置全过程文档

    ReviewBoard是一款强大的代码审查工具,它提供了一个web界面,让开发者提交代码变更请求,同事或领导可以查看并提供反馈。在`reviewBoard安装过程.docx`中,你将学习如何下载ReviewBoard,配置数据库连接,安装必要...

    代码审查CodeReview的最佳实践

    我一直认为CodeReview(代码审查)是软件开发中的最佳实践之一,可以有效提高整体代码质量,及时发现代码中可能存在的问题。包括像Google、微软这些公司,CodeReview都是基本要求,代 我一直认为CodeReview(代码...

    代码review.ppt

    了解代码review做的ppt,可以帮助人理解代码review的概念

    基于Gitlab的代码审查流程(Code-Review)方案

    Git是一种先进的分布式版本控制系统,由林纳斯·托瓦兹开发,旨在取代较为传统的版本控制系统,如SVN和Mercurial...同时,利用GitLab-CI进行持续集成,可以确保每一次代码变更后快速得到反馈,以保持代码库的健康状态。

    code review代码检测原理

    **代码审查(Code Review)是软件开发过程中的一个重要环节,旨在提高代码质量,发现潜在的错误,提升团队协作效率,并确保代码遵循最佳实践和项目规范。本文将深入探讨代码审查的原理、步骤以及如何有效地执行代码...

    静态测试方法之代码审查(CodeReview)的清单

    静态测试方法之代码审查(CodeReview)的清单。代码审查可以帮助提高代码质量,避免由于代码习惯而造成的bug。下面列出的这些要点因该可以作为大部分代码审查的指导,如果是Java应用的话,这些建议应该被视作最佳实践...

    review board自动提交代码

    是reviewboard为实现自动化提交代码的脚本

    IDEA代码检视插件Code Review Helper(支持团队协同)

    标题提到的"IDEA代码检视插件Code Review Helper"是针对IntelliJ IDEA集成开发环境的一款强大工具,旨在提升代码审查的效率和质量。代码审查是软件开发中的一个关键环节,通过它,团队成员可以互相检查彼此的代码,...

    jupiter代码review工具

    Jupiter是一款专用于代码审查的工具,旨在帮助软件开发团队提高代码质量和确保代码规范性。代码审查是软件开发过程中的重要环节,它可以帮助发现潜在的错误、提升代码可读性,并促进团队成员之间的交流与学习。...

    Andriod 代码Review工具 插件

    提供一个很好的代码Review工具。可以设置4个级别,可以对优秀代码进行学习,还可以对有问题的代码进行复查。

    Java项目开发代码Review常见问题实例.doc

    在Java项目开发中,代码审查(Code Review)是确保代码质量、遵循最佳实践以及提高团队协作效率的关键步骤。本文档将深入探讨在Code Review过程中常见的问题,并提供实例分析,以帮助开发者避免这些问题。 **第一章...

    代码review准则

    开发编写代码需要知道的基本准则,仅供参考,非原创,如有雷同侵权请联系作者删除 纯属分享

    C++ 代码审查 reviewboard

    Reviewboard 是一个流行的开源代码审查工具,支持多种版本控制系统如 SVN、CVS 等,并且具有友好的用户界面。本文将详细介绍如何在 Windows 环境下部署和配置 Reviewboard 服务器,以便于进行 C++ 代码审查。 #### ...

Global site tag (gtag.js) - Google Analytics