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

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

(0)

相关推荐

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

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

  • rollup打包引发对JS模块循环引用思考

    目录 引言 背景1 背景2 commonjs es modules 总结 引言 最近在项目中使用了typescript + rollup,满心欢喜测试打包结果的时候,发现打包出来的文件竟然无法运行,具体报错如下: throw new ERR_INVALID_ARG_TYPE('superCtor', 'Function', superCtor); ^ TypeError [ERR_INVALID_ARG_TYPE]: The "superCtor" argument must be o

  • 谈谈因Vue.js引发关于getter和setter的思考

    起因 当我打印出Vue实例下的data对象里的属性时,发现了一个有趣的事情: 它的每个属性都有两个相对应的get和set方法,我觉的这是多此一举的,于是去网上查了查Vue双向绑定的实现原理,才发现它和Angular.js双向绑定的实现原理完全不同,Angular是用的数据脏检测,当Model发生变化,会检测所有视图是否绑定了相关数据,再更改视图.而Vue使用的发布订阅模式,是点对点的绑定数据. Vue的数据绑定只有两个步骤,compile=>link. 我一直在想,vue是通过什么去监听用户对M

  • 一道面试题引发的对javascript类型转换的思考

    最近群里有人发了下面这题: 实现一个函数,运算结果可以满足如下预期结果: add(1)(2) // 3 add(1, 2, 3)(10) // 16 add(1)(2)(3)(4)(5) // 15 对于一个好奇的切图仔来说,忍不住动手尝试了一下,看到题目首先想到的是会用到高阶函数以及 Array.prototype.reduce() 高阶函数(Higher-order function):高阶函数的意思是它接收另一个函数作为参数.在 javascript 中,函数是一等公民,允许函数作为参数或

  • mysql CPU高负载问题排查

    MySQL导致的CPU高负载问题 今天下午发现了一个MySQL导致的向上服务器负载高的问题,事情的背景如下: 在某个新服务器上,新建了一个MySQL的实例,该服务器上面只有MySQL这一个进程,但是CPU的负载却居高不下,使用top命令查询的结果如下: [dba_mysql@dba-mysql ~]$ top top - 17:12:44 up 104 days, 20 min, 2 users, load average: 1.06, 1.02, 1.00 Tasks: 218 total,

  • Webpack中使用环境变量的各种正确姿势

    目录 写在前边 业务代码使用环境变量 使用webpack.DefinePlugin插件在业务代码中注入环境变量 webpack.DefinePlugin引发的思考 definePlugin所谓的"环境变量"实现方式 JSON.stringify()处理环境变量 构建过程中使用环境变量 传统环境变量方法使用webpack构建过程环境变量. 总结 写在前边 你还在为Webpack中各种打包配置而烦恼吗? 今天我们来聊聊webpack中注入环境变量的各种姿势,或者你会觉得注入环境变量通过命令

  • Python中for循环可迭代对象迭代器及生成器源码学习

    目录 问题: 1. 迭代 1.1 可迭代对象Iterable 1.2 迭代器Iterator 1.3 for循环 1.3.1 iter()方法和next()方法 1.3.2 iter()和__iter__() 1.3.3 next()和__next__() 1.3.4 自定义类实现__iter__()和__next__() 1.3.5 探究for循环 2 生成器 2.1 获取生成器 2.2 next(生成器) 2.3 生成器和迭代器 2.4 生成器解析式 问题: 之前在学习list和dict相关

  • 关于ORACLE通过file_id与block_id定位数据库对象遇到的问题引发的思考

    在ORACLE中,我们可以通过file_id(file#)与block_id(block#)去定位一个数据库对象(object).例如,我们在10046生成的trace文件中file#=4 block#=266 blocks=8,那么我可以通过下面两个SQL去定位对象 SQL 1:此SQL效率较差,执行时间较长. SELECT OWNER, SEGMENT_NAME, SEGMENT_TYPE, TABLESPACE_NAME FROM DBA_EXTENTS WHERE FILE_ID =&F

  • 有关.NET参数传递的方式引发的思考

    下面就简单的介绍一下.NET的一些常用参数用法,如有不足还望指正,也欢迎大家在下面留言讨论,分享自己的见解. 一.DotNet参数概述: .NET中参数(形式参数)变量是方法或索引器声明的一部分,而实参是调用方法或索引器时使用的表达式. 在CLR中,默认的情况下所有的方法参数都是传值的.在传递引用类型的对象时,对一个对象的引用会传递给方法.这里的船引用本身是以传值的方式传给方法的.这也意味着方法能够修改对象,而调用者能看到这些修改.对于值类型的实例,传给方法的实例的一个副本.意味着方法将获得它专

  • 浅谈Python由__dict__和dir()引发的一些思考

    关于__dict__和dir()的区别和作用请参考这篇文章: 基于Python __dict__与dir()的区别详解 说下我当时遇到的问题: class Demo: def __init__(self, name, age): self.name = name self.age = age def func(self): print('Hello {0}'.format(self.name)) >>> d1 = Demo('Pythoner', 24) >>> has

随机推荐