- 浏览: 776510 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
Fanatic357:
同问,请问这个 曲线 是用什么工具 监测得到的?
RocketMQ性能压测分析 -
sunshine_love:
8核 16G, 单master TPS 4w+,2m-2s- ...
RocketMQ性能压测分析 -
assertmyself:
很好,,获益良多!
jstack和线程dump分析 -
zhaoxiaoxiao:
非常赞,帮助理解了问题。今天也是遇到了这样的问题
hessian序列化bug -
wjg_java:
打不开 宕机了
博客停止更新
单例模式?多么简单!也许吧,可是要通过简单的现象,看到问题的本质,就比较难,知其然而不知其所以然,这种态度不好。
一:看看最简单的
public class SingleInstance{
private static instance=new SinleInstance();
public static SingleInstance getInstance(){
return instance;
}
}
这个很明了,也确实会返回唯一的实例,但是如果我永远都不会用到SingleInstance.getInstance(),instance这个对象却一直存在,占用了内存空间,浪费。
二:那就不先实例化
public class SingleInstance{
private static SingleInstance instance;
public static SingleInstance getInstance(){
if(instance==null){
instance=new SingleInstance();
}
return instance;
}
}
表面上,这个似乎是可行的。但是很显然,在多线程并发环境中,可能会创建两个instance,也有可能一个线程拿到的instance是一个不完整的实例。
三:好吧,那我加上同步总可以吧
public class SingleInstance{
private static SingleInstance instance;
public static synchronized SingleInstance getInstance(){
if(instance==null){
instance=new SingleInstance();
}
return instance;
}
}
这个虽然不会有内存上的浪费,但是有同步锁住了整个class,会降低系统的性能。按照数据结构的说法,就是一个提高了空间复杂度,一个提高了时间复杂度。
四:最佳实践
比较好的做法
public class SingleInstance {
private static class InstanceHolder {
public static final SingleInstance instance = new SingleInstance();
}
public static SingleInstance getInstance() {
return InstanceHolder.instance;
}
}
这种Holder做法保证了实例在用到的时候才会创建,而且创建的实例肯定是唯一的。
五:所谓的DCL
下面这种做法是广为流传的所谓DCL(双重检查锁定)做法,这是没有正确理解java内存模型造成的,只保证了创建实例的原子性,而没有解决共享资源的可见性。
public class SingleInstance{
private static SingleInstance instance;
public static SingleInstance getInstance(){
if(instance==null){
synchronized(SingleInstance.class){
if(instance==null){
instance=new SingleInstance();
}
}
}
return instance;
}
}
表面上看,这个似乎确实没有什么问题呢,其实还是有问题的,虽然概率比较小,要深刻的理解为什么,必须要对java内存模型比较熟悉。对于instance的创建,某个时刻某个线程拿到了同步锁,将instance创建了,并且及时写回了主存。但是对于另一个线程而言,它不能及时“看到”instance变量的最新值。它的instance可能是工作内存里的而不是主内存里的,甚至更为糟糕的情况是,instance是主内存的,可它的某些属性是工作内存的,这会导致程序发生莫名其妙不可捉摸的错误。在平台级框架里,这会造成系统在高并发环境下不稳定,甚至造成致命的错误 。
评论
1.线程A进入getInstance()方法;
2.线程B进入getInstance()方法;
3.线程A执行,if(instance==null),从主存拷贝instance到工作内存;
4.线程B执行,if(instance==null),从主存拷贝instance到工作内存;
5.线程A进入临界区:
synchronized(SingleInstance.class){ if(instance==null){ instance=new SingleInstance(); } }
执行完后,线程A同步至主存,
接下来就是楼主担心的吗?
就是“该线程放弃锁后,后续执行的线程执行if(instance==null)时,instance不一定是主内中最新的”
可问题是,如果对SingleInstance.class加锁,上述步骤中的2,4会发生吗?即使在一个高度并发的情况下?
再问:
如果对SingleInstance.class加锁,线程A获取到了锁,那么线程B是不是就需要等待,即使高度并发?
如果线程B需要等待,那么线程A执行的就是原子性动作,等线程B去执行时,获取到的自然就是线程A同步好的instance,应该不会出现楼主担心的情况,不知道是不是这样?
我测试了一下,你说的是对的,不会出现楼主担心的情况。
但是,锁住的不是整个方法,所以上述步骤中的2 发生,即每次读的值 是工作内存的,可能是和其他线程修改后的值不一样,即旧值。
步骤 4不会发生。每次 4 都是从主内存中拷贝一份新的。即其他线程修改后的新值。
同意,这里如果你不去调用getInstance()的话应该是不会初始化的。 当 setstatic, putstatic, invokestatic等指令被调用的时候才会去初始化类。 所以如果不去调用getInstance()方法的话,instance将仍然维持零值NULL.
请主人confirm.
private static SingleInstance instance;
加volatile可以解决问题
if(instance==null){ synchronized(SingleInstance.class){ if(instance==null){ instance=new SingleInstance(); } } }
临界区这里的instance,难道不是从主内存拷贝到工作内存里面的吗?
进入临界区这里的线程,instance是从主内存拷贝到工作内存。
该线程放弃锁后,后续执行的线程执行 if(instance==null)时,instance不一定是主内中最新的。
楼主的意思是:?
1.线程A进入getInstance()方法;
2.线程B进入getInstance()方法;
3.线程A执行,if(instance==null),从主存拷贝instance到工作内存;
4.线程B执行,if(instance==null),从主存拷贝instance到工作内存;
5.线程A进入临界区:
synchronized(SingleInstance.class){ if(instance==null){ instance=new SingleInstance(); } }
执行完后,线程A同步至主存,
接下来就是楼主担心的吗?
就是“该线程放弃锁后,后续执行的线程执行if(instance==null)时,instance不一定是主内中最新的”
可问题是,如果对SingleInstance.class加锁,上述步骤中的2,4会发生吗?即使在一个高度并发的情况下?
再问:
如果对SingleInstance.class加锁,线程A获取到了锁,那么线程B是不是就需要等待,即使高度并发?
如果线程B需要等待,那么线程A执行的就是原子性动作,等线程B去执行时,获取到的自然就是线程A同步好的instance,应该不会出现楼主担心的情况,不知道是不是这样?
if(instance==null){ synchronized(SingleInstance.class){ if(instance==null){ instance=new SingleInstance(); } } }
临界区这里的instance,难道不是从主内存拷贝到工作内存里面的吗?
进入临界区这里的线程,instance是从主内存拷贝到工作内存。
该线程放弃锁后,后续执行的线程执行 if(instance==null)时,instance不一定是主内中最新的。
确实不太理解这段代码会出现什么样的问题,为什么会有这样的问题!作者有心的话可以写出一个浅显易懂的文章分享!
if(instance==null){ synchronized(SingleInstance.class){ if(instance==null){ instance=new SingleInstance(); } } }
临界区这里的instance,难道不是从主内存拷贝到工作内存里面的吗?
进入临界区这里的线程,instance是从主内存拷贝到工作内存。
该线程放弃锁后,后续执行的线程执行 if(instance==null)时,instance不一定是主内中最新的。
if(instance==null){ synchronized(SingleInstance.class){ if(instance==null){ instance=new SingleInstance(); } } }
临界区这里的instance,难道不是从主内存拷贝到工作内存里面的吗?
发表评论
-
dubbo问题总结
2012-03-14 10:00 2983任何诡异的现象必然能找到问题原因,程序是不会骗人的 ... -
memcached客户端源码分析
2011-09-08 17:28 19950memcached的java客户端有好 ... -
jstack和线程dump分析
2011-05-12 13:48 180163一:jstack jstack命令的语法格式: js ... -
说说new Integer和Integer.valueOf
2010-11-11 15:04 6596看看这两个语句 Integer a=new Integ ... -
线程安全总结(二)
2010-11-11 12:36 5609关于线程安全总结(-)请看 http://www.iteye ... -
java线程安全总结
2010-11-09 20:48 15639最近想将java基 ... -
hadoop架构
2010-09-07 19:41 2685该文章我转自IBM开发者社区 ... -
HashMap深入分析
2010-09-03 19:36 5830java.util.HashMap是很常见的 ... -
CountDownLatch
2010-09-02 20:03 2964java的并发包真 ... -
ThreadPoolExecutor相关类的分析
2010-09-02 19:27 4598一:ThreadPoolExecutor ... -
随便说说
2010-09-01 19:29 2100这两天给系统 ... -
一波三折的rmi调用
2010-08-18 18:02 9854很久以前写了基于rmi的分布式java程序,现 ... -
java内存查看与分析
2010-08-07 17:03 22482业界有很多强 ... -
java动态代理之cglib
2010-06-22 17:27 2794cglib是一个 ... -
java动态代理随笔二
2010-06-22 16:29 1879jdk的动态代 ... -
java动态代理随笔一
2010-06-22 14:49 2077先说一下java class的加载机制和与cla ... -
关于hashcode和equals
2010-04-19 14:58 3389前几天有个同事问我,String a=" ... -
建设银行对接(五)
2010-02-09 17:34 2559public static void testVerify ... -
建设银行对接(四)
2010-02-09 17:32 3091上接“建设银行对接(三)”,javaeye的文章字数限制也太少 ... -
建设银行对接(三)
2010-02-09 17:24 3474前面两章请见我的博客 对建行返回的数据进行数字签名 ...
相关推荐
首先,我们要理解什么是“单例模式”。单例模式是一种设计模式,确保一个类只有一个实例,并提供全局访问点。这种模式常用于控制共享资源,如数据库连接、日志文件或线程池。在C++中,单例通常通过私有构造函数和...
再来说说**单例模式**。单例模式确保一个类只有一个实例,并提供一个全局访问点。在PHP中,单例常用于管理共享资源,如数据库连接、缓存系统等。通过单例,我们可以控制类的实例化过程,防止因过多实例化导致的资源...
C#中实现单例模式通常有懒汉式和饿汉式两种方式,前者在第一次使用时初始化,后者在类加载时就完成初始化。 接下来是工厂模式,它是创建型设计模式的一种,提供了创建对象的接口,但隐藏了具体的创建过程。在C#中,...
接着是单例模式,确保一个类只有一个实例,并提供全局访问点。在资源管理或者需要全局共享状态的情况下,单例模式非常有用。但要注意,过度使用单例可能导致代码过于紧密耦合,不易测试和维护。 接下来,我们来看看...
【06期】单例模式有几种写法? 【07期】Redis中是如何实现分布式锁的? 【08期】说说Object类下面有几种方法呢? 【09期】说说hashCode() 和 equals() 之间的关系? 【10期】Redis 面试常见问答 【11期】分布式...
- **Struts1**中的Action对象在整个请求周期内是单例模式的,这意味着所有请求都会共用同一个Action实例,这可能会导致线程安全问题。 - 相比之下,**Struts2**为每个请求创建一个新的Action实例,这不仅提高了安全...
1. **单例操作**:尽可能将多步操作封装为单次命令,如Redis的`INCRBYFLOAT`命令可以实现浮点数的原子性增加。 2. **Lua脚本**:通过Lua脚本来组合多个操作,并利用`EVAL`命令执行,保证整个脚本的原子性。例如,...
- **常见的设计模式**:单例模式、工厂模式、策略模式、观察者模式等。 - **设计模式的六大原则**:单一职责原则、开放封闭原则、里氏替换原则、依赖倒置原则、接口隔离原则、迪米特法则。 - **常见的单例模式**:...
在SpringMVC中,控制器通常被定义为单例模式,这意味着所有请求都共享同一个控制器实例。因此,需要注意解决线程安全问题。主要通过以下几种方式: - **非线程安全的成员变量**:确保控制器中不包含任何实例级别的...
一说到编程模型或者开发模式,大家应该第一反应是23 种开发模型:单例、工厂、发布订阅... 吧啦吧啦。实际我也是,但是这次想说的不是他们,而是声明式编程(declarative programming)范式和陈述式编程(imperative...
线程 单例模式下的线程安全http://blog.sina.com.cn/s/blog_75247c770100yxpb.html map集合 集合数据结构及方法的使用 多线程 线程进程 线程状态 线程状态的改变 sql :临时表、游标、存贮过程、触发机制...
单例模式是一种设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。其实现通常包括私有构造函数、静态实例变量以及静态工厂方法。 #### 六、什么是动态,举例说明 动态性是指程序在运行时能够改变其结构...
超级有影响力的Java面试题大全文档 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。...
如何控制两种框架中的单例模式? 74 73. Spring 75 73.1. Spring 简介 75 73.2. 为什么要用Spring? 76 73.3. spring工作机制或工作原理 76 73.4. Spring是什么?根据你的理解详细谈谈你的见解。 76 73.5. 项目中如何...
- **单例模式**:确保一个类只有一个实例,并提供一个全局访问点。 - **工厂模式**:定义创建对象的接口,但让子类决定实例化哪个类。 - **观察者模式**:定义了一种一对多的依赖关系,当一个对象的状态发生改变时,...
- **singleton**:单例模式,容器中只有一个Bean实例。 - **prototype**:原型模式,每次请求都会创建一个新的Bean实例。 - **request**:每个HTTP请求都有一个Bean实例。 - **session**:每个HTTP Session都有一个...
采用的是单例模式。 之后具体的调用,相关代码如下: if FindClass(fClsPer.ClassName) <> nil then begin tmpPer := TPersistentClass(FindClass(fClsPer.ClassName)).Create; Supports(tmpPer, StringToGUID('...