志在指尖
用双手敲打未来

单例模式没你想的那么简略

网上处处都是懒汉,饿汉形式。给两个Demo就算过去了吧。
饿汉单例形式:在类加载的时分,就开端实例化了。
publicclassHungrySingleton{privatestaticHungrySingletonone=newHungrySingleton();privateHungrySingleton(){}publicstaticHungrySingletongetInstance(){returnone;
}publicstaticvoidmain(String[]args){
HungrySingletonone1=HungrySingleton.getInstance();
HungrySingletonone2=HungrySingleton.getInstance();
System.out.println(one1==one2);
}
}
懒汉形式:在第一次获取实例化对象的时分,开端实例化。
publicclassLazySingleton{privatestaticLazySingletonone=null;privateLazySingleton(){
}publicstaticLazySingletongetInstance(){if(one==null){
one=newLazySingleton();
}returnone;
}publicstaticvoidmain(String[]args){
LazySingletonone1=LazySingleton.getInstance();
LazySingletonone2=LazySingleton.getInstance();
System.out.println(one1==one2);
}
}
无论何种形式先要把结构函数给私有化,否则就变成了“勤快汉”形式了;这名字是我瞎编的。
饿汉形式典型用法:Spring中IOC容器ApplictionContext,连接池.
懒汉形式典型用法:不知道啊。
饿汉形式没缺点,最多也就在没使用的时分,分配个内存空间。
下面着重说说懒汉形式,所以来个分割线吧。
=================================
懒汉单例形式的线程安全
上面的懒汉形式有线程安全问题,便是多个线程在一起履行的时分,怎样确保LazySingleton只被实例化了一次。
线程类:
publicclassExectorThreadimplementsRunnable{
@Overridepublicvoidrun(){
LazySimpleSingletonone=LazySimpleSingleton.getInstance();
System.out.println(Thread.currentThread().getName()+”:”+one);
}
}
单例类:
publicclassLazySimpleSingleton{privatestaticLazySimpleSingletonone=null;privateLazySimpleSingleton(){
}publicstaticLazySimpleSingletongetInstance(){if(one==null){
one=newLazySimpleSingleton();
}returnone;
}
}
测验类:
publicclassLazyTest{publicstaticvoidmain(String[]args){
Threadt1=newThread(newExectorThread());
Threadt2=newThread(newExectorThread());
t1.start();
t2.start();
System.out.println(“end”);
}
}
第一个线程把one实例化完结之后,还没有来得及刷新到内存,第二个线程就把one读入内存,又进行了一次实例化。
最简略的办法便是给实例化办法getInstance()增加一个synchronized.
修改后代码如下
publicclassLazySimpleSingleton{privatestaticLazySimpleSingletonone=null;privateLazySimpleSingleton(){
}publicstaticsynchronizedLazySimpleSingletongetInstance(){if(one==null){
one=newLazySimpleSingleton();
}returnone;
}
这种形式有一个功能问题;比如100个线程在一起调用getInstance()的时分,99个全部都堵塞在这个方位了,
包含one现已不是空值的时分,仍然在堵塞中;改造上面的代码,让现已实例化之后的线程不在堵塞。
1publicclassLazySimpleSingleton{23privatestaticLazySimpleSingletonone=null;45privateLazySimpleSingleton(){6}7publicstaticLazySimpleSingletongetInstance(){8//索前判别是否实例化了,实例化了就不用进入synchronized中了9if(one==null){10synchronized(LazySimpleSingleton.class){11//上面one==null了,不代表此刻还是null12if(one==null){13one=newLazySimpleSingleton();14}15returnone;16}17}18returnone;19}20}
反射损坏单例
以饿汉单例的Demo为比如进行改造。
1publicclassHungrySingleton{23privatestaticHungrySingletonone=newHungrySingleton();45privateHungrySingleton(){}67publicstaticHungrySingletongetInstance(){8returnone;9}1011publicstaticvoidmain(String[]args)throwsException{12HungrySingletonone1=HungrySingleton.getInstance();13Constructorconstructor=HungrySingleton.class.getDeclaredConstructor(null);14//强制拜访结构器,包含私有成员15constructor.setAccessible(true);16HungrySingletonone2=(HungrySingleton)constructor.newInstance();17System.out.println(one1==one2);18}19}
打印成果显现false.说明被实例化了两次;修改代码如下。
publicclassHungrySingleton{privatestaticHungrySingletonone=newHungrySingleton();privateHungrySingleton(){if(one!=null){
thrownewRuntimeException(“现已实例化过了,本次实例化失败”);
}}publicstaticHungrySingletongetInstance(){returnone;
}publicstaticvoidmain(String[]args)throwsException{
HungrySingletonone1=HungrySingleton.getInstance();
Constructorconstructor=HungrySingleton.class.getDeclaredConstructor(null);//强制拜访结构器,包含私有成员constructor.setAccessible(true);
HungrySingletonone2=(HungrySingleton)constructor.newInstance();
System.out.println(one1==one2);
}
}
打印成果:
序列化损坏单例形式
以饿汉形式为例:
publicclassSeriableSingletonimplementsSerializable{publicfinalstaticSeriableSingletonone=newSeriableSingleton();privateSeriableSingleton(){
}publicstaticSeriableSingletongetInstance(){returnone;
}
}
测验类:
1publicclassSeriableSingletonTest{2publicstaticvoidmain(String[]args){3SeriableSingletons1=null;4SeriableSingletons2=SeriableSingleton.getInstance();56FileOutputStreamfos=null;7try{8fos=newFileOutputStream(“one.obj”);9ObjectOutputStreamoos=newObjectOutputStream(fos);10oos.writeObject(s2);11oos.flush();12oos.close();1314FileInputStreamfis=newFileInputStream(“one.obj”);15ObjectInputStreamois=newObjectInputStream(fis);16s1=(SeriableSingleton)ois.readObject();17ois.close();1819System.out.println(s1==s2);2021}catch(Exceptione){2223}24}25}
显现成果是false.
这个问题很好办,加一行代码的事情。
1publicclassSeriableSingletonimplementsSerializable{2publicfinalstaticSeriableSingletonone=newSeriableSingleton();34privateSeriableSingleton(){5}67publicstaticSeriableSingletongetInstance(){8returnone;9}1011privateObjectreadResolve(){
12returnone;
13}14}
上面赤色便是增加的一个办法。
这是一个古怪的现象,直接从源码中找答案吧。
readObject()源码
readObject0()源码
privateObjectreadObject0(booleanunshared)throwsIOException{
…caseTC_OBJECT:returncheckResolve(readOrdinaryObject(unshared));
……..
}Java
上面代码去除了一些无用代码。
持续看readOrdinaryObject()源码
privateObjectreadOrdinaryObject(booleanunshared)throwsIOException
{//判别是否有readResolve()办法if(obj!=null&&handles.lookupException(passHandle)==null&&desc.hasReadResolveMethod())
{
Objectrep=desc.invokeReadResolve(obj);
}returnobj;
}
invokeReadResolve()源码
ObjectinvokeReadResolve(Objectobj)throwsIOException,UnsupportedOperationException
{
……….if(readResolveMethod!=null){try{//利用反射调用readResolve()办法returnreadResolveMethod.invoke(obj,(Object[])null);
}
}
}
由于JDK在readObject()时分,判别了有没有readResolve()办法,如果有的话就履行这个办法,没有就不履行了,咱们充分利用了这个特点,给他直接回来了一个one对象;
所以就不履行实例化了。其实这个当地在readObject()时分实例化了一次,只不过新创建的对象没有被回来而已。

未经允许不得转载:IT技术网站 » 单例模式没你想的那么简略
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!

 

志在指尖 用双手敲打未来

登录/注册IT技术大全

热门IT技术

C#基础入门   SQL server数据库   系统SEO学习教程   WordPress小技巧   WordPress插件   脚本与源码下载