- 浏览: 1336129 次
- 性别:
- 来自: 湖南澧縣
文章分类
最新评论
-
虾米小尹:
不行啊!2.2-0.25=1.9500000000000002 ...
JavaScript浮点数运算 —— 精度问题 -
heluping000000:
引用String a= "abc",首先在 ...
String,到底创建了多少个对象? -
mack:
谢谢分享matcher.appendReplacement(s ...
string.replaceAll()中的特殊字符($ \)与matcher.appendReplacement -
wzt3309:
完全理解,比网上其他资料都要详细
String,到底创建了多少个对象? -
u014771876:
Java中十六进制转换 Integer.toHexString()
首先要解释一下什么是延迟加载,延迟加载就是等到真真使用的时候才去创建实例,不用时不要去创建。
从速度和反应时间角度来讲,非延迟加载(又称饿汉式)好;从资源利用效率上说,延迟加载(又称懒汉式)好。
下面看看几种常见的单例的设计方式:
第一种:非延迟加载单例类
public class Singleton { private Singleton() {} private static final Singleton instance = new Singleton(); public static Singleton getInstance() { return instance; } }
第二种:同步延迟加载
public class Singleton { private static Singleton instance = null; private Singleton() {} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
第三种:双重检测同步延迟加载
为处理原版非延迟加载方式瓶颈问题,我们需要对 instance 进行第二次检查,目的是避开过多的同步(因为这里的同步只需在第一次创建实例时才同步,一旦创建成功,以后获取实例时就不需要同获取锁了),但在Java中行不通,因为同步块外面的if (instance == null)可能看到已存在,但不完整的实例。JDK5.0以后版本若instance为volatile则可行:
public class Singleton { private volatile static Singleton instance = null; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) {// 1 if (instance == null) {// 2 instance = new Singleton();// 3 } } } return instance; } }
双重检测锁定失败的问题并不归咎于 JVM 中的实现 bug,而是归咎于 Java 平台内存模型。内存模型允许所谓的“无序写入”,这也是失败的一个主要原因。
无序写入:
为解释该问题,需要重新考察上述清单中的 //3 行。此行代码创建了一个 Singleton 对象并初始化变量 instance 来引用此对象。这行代码的问题是:在 Singleton 构造函数体执行之前,变量 instance 可能成为非 null 的,即赋值语句在对象实例化之前调用,此时别的线程得到的是一个还会初始化的对象,这样会导致系统崩溃。
什么?这一说法可能让您始料未及,但事实确实如此。在解释这个现象如何发生前,请先暂时接受这一事实,我们先来考察一下双重检查锁定是如何被破坏的。假设代码执行以下事件序列:
1、线程 1 进入 getInstance() 方法。
2、由于 instance 为 null,线程 1 在 //1 处进入 synchronized 块。
3、线程 1 前进到 //3 处,但在构造函数执行之前,使实例成为非 null。
4、线程 1 被线程 2 预占。
5、线程 2 检查实例是否为 null。因为实例不为 null,线程 2 将 instance 引用返回给一个构造完整但部分初始化了的 Singleton 对象。
6、线程 2 被线程 1 预占。
7、线程 1 通过运行 Singleton 对象的构造函数并将引用返回给它,来完成对该对象的初始化。
为展示此事件的发生情况,假设代码行 instance =new Singleton(); 执行了下列伪代码:
mem = allocate(); //为单例对象分配内存空间.
instance = mem; //注意,instance 引用现在是非空,但还未初始化
ctorSingleton(instance); //为单例对象通过instance调用构造函数
这段伪代码不仅是可能的,而且是一些 JIT 编译器上真实发生的。执行的顺序是颠倒的,但鉴于当前的内存模型,这也是允许发生的。JIT 编译器的这一行为使双重检查锁定的问题只不过是一次学术实践而已。
如果真像这篇文章:http://dev.csdn.net/author/axman/4c46d233b388419e9d8b025a3c507b17.html所说那样的话,1.2或以后的版本就不会有问题了,但这个规则是JMM的规范吗?谁能够确认一下。
确实,在JAVA2(以jdk1.2开始)以前对于实例字段是直接在主储区读写的.所以当一个线程对resource进行分配空间,
初始化和调用构造方法时,可能在其它线程中分配空间动作可见了,而初始化和调用构造方法还没有完成.
但是从JAVA2以后,JMM发生了根本的改变,分配空间,初始化,调用构造方法只会在线程的工作存储区完成,在没有
向主存储区复制赋值时,其它线程绝对不可能见到这个过程.而这个字段复制到主存区的过程,更不会有分配空间后
没有初始化或没有调用构造方法的可能.在JAVA中,一切都是按引用的值复制的.向主存储区同步其实就是把线程工作
存储区的这个已经构造好的对象有压缩堆地址值COPY给主存储区的那个变量.这个过程对于其它线程,要么是resource
为null,要么是完整的对象.绝对不会把一个已经分配空间却没有构造好的对象让其它线程可见.
另一篇详细分析文章:http://www.iteye.com/topic/260515
第四种:使用ThreadLocal修复双重检测
借助于ThreadLocal,将临界资源(需要同步的资源)线程局部化,具体到本例就是将双重检测的第一层检测条件 if (instance == null) 转换为了线程局部范围内来作。这里的ThreadLocal也只是用作标示而已,用来标示每个线程是否已访问过,如果访问过,则不再需要走同步块,这样就提高了一定的效率。但是ThreadLocal在1.4以前的版本都较慢,但这与volatile相比却是安全的。
public class Singleton { private static final ThreadLocal perThreadInstance = new ThreadLocal(); private static Singleton singleton ; private Singleton() {} public static Singleton getInstance() { if (perThreadInstance.get() == null){ // 每个线程第一次都会调用 createInstance(); } return singleton; } private static final void createInstance() { synchronized (Singleton.class) { if (singleton == null){ singleton = new Singleton(); } } perThreadInstance.set(perThreadInstance); } }
第五种:使用内部类实现延迟加载
为了做到真真的延迟加载,双重检测在Java中是行不通的,所以只能借助于另一类的类加载加延迟加载:
public class Singleton { private Singleton() {} public static class Holder { // 这里的私有没有什么意义 /* private */static Singleton instance = new Singleton(); } public static Singleton getInstance() { // 外围类能直接访问内部类(不管是否是静态的)的私有变量 return Holder.instance; } }
单例测试
下面是测试单例的框架,采用了类加载器与反射。
注,为了测试单便是否为真真的单例,我自己写了一个类加载器,且其父加载器设置为根加载器,这样确保Singleton由MyClassLoader加载,如果不设置为根加载器为父加载器,则默认为系统加载器,则Singleton会由系统加载器去加载,但这样我们无法卸载类加载器,如果加载Singleton的类加载器卸载不掉的话,那么第二次就不能重新加载Singleton的Class了,这样Class不能得加载则最终导致Singleton类中的静态变量重新初始化,这样就无法测试了。
下面测试类延迟加载的结果是可行的,同样也可用于其他单例的测试:
public class Singleton { private Singleton() {} public static class Holder { // 这里的私有没有什么意义 /* private */static Singleton instance = new Singleton(); } public static Singleton getInstance() { // 外围类能直接访问内部类(不管是否是静态的)的私有变量 return Holder.instance; } } class CreateThread extends Thread { Object singleton; ClassLoader cl; public CreateThread(ClassLoader cl) { this.cl = cl; } public void run() { Class c; try { c = cl.loadClass("Singleton"); // 当两个不同命名空间内的类相互不可见时,可采用反射机制来访问对方实例的属性和方法 Method m = c.getMethod("getInstance", new Class[] {}); // 调用静态方法时,传递的第一个参数为class对象 singleton = m.invoke(c, new Object[] {}); c = null; cl = null; } catch (Exception e) { e.printStackTrace(); } } } class MyClassLoader extends ClassLoader { private String loadPath; MyClassLoader(ClassLoader cl) { super(cl); } public void setPath(String path) { this.loadPath = path; } protected Class findClass(String className) throws ClassNotFoundException { FileInputStream fis = null; byte[] data = null; ByteArrayOutputStream baos = null; try { fis = new FileInputStream(new File(loadPath + className.replaceAll("\\.", "\\\\") + ".class")); baos = new ByteArrayOutputStream(); int tmpByte = 0; while ((tmpByte = fis.read()) != -1) { baos.write(tmpByte); } data = baos.toByteArray(); } catch (IOException e) { throw new ClassNotFoundException("class is not found:" + className, e); } finally { try { if (fis != null) { fis.close(); } if (fis != null) { baos.close(); } } catch (Exception e) { e.printStackTrace(); } } return defineClass(className, data, 0, data.length); } } class SingleTest { public static void main(String[] args) throws Exception { while (true) { // 不能让系统加载器直接或间接的成为父加载器 MyClassLoader loader = new MyClassLoader(null); loader .setPath("D:\\HW\\XCALLC16B125SPC003_js\\uniportal\\service\\AAA\\bin\\"); CreateThread ct1 = new CreateThread(loader); CreateThread ct2 = new CreateThread(loader); ct1.start(); ct2.start(); ct1.join(); ct2.join(); if (ct1.singleton != ct2.singleton) { System.out.println(ct1.singleton + " " + ct2.singleton); } // System.out.println(ct1.singleton + " " + ct2.singleton); ct1.singleton = null; ct2.singleton = null; Thread.yield(); } } }
评论
最主要的一点就是,线程可能从任何点切换,这个点可能是某两个语句中,也有可能是从语句中中断,这也是完全有可能的。
如果这样,那第二种的时候,我也在new的时候切换,那不是也不能保证线程安全了吗?
ps:我很少做多线程的东西,不是很了解线程安全
if (instance == null) { //0 synchronized (Singleton.class) {// 1 if (instance == null) {// 2 instance = new Singleton();// 3 //这里是不是只有instance初始化完整,他才会返回? return instance;// 4 } }
是不是就能解决问题?
肯定是不行的,因为本质你还是有这一句 “instance = new Singleton(); ”这样的赋值语句,所以还是有可能从这条语句中中断切换到另外一线程,而不是说从“return instance;”语句中切换到另一线程,不过我也不知道 return instance; 这样的语句是不是原子性的,但至少你这种必法是起不了作用的。
最主要的一点就是,线程可能从任何点切换,这个点可能是某两个语句中,也有可能是从语句中中断,这也是完全有可能的。
最大的原因就是 Object o = new Object(); 这样的赋值在 JMM (Java 内存模型)里不一条指令来完成,所以表面上看上去是一条,但实质是由 一串指令 来完成,所以就有可能从 这些指令中切换到另一个线程。所以就导致双重检测失效
这样的写法是能避免无序写入的问题。因为别的线程进入不了方法体,除非当前线程释放锁。这样就能确保实例化完成。我的理解对吗?
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
}
听说在1.5版本后修改了JMM,应该是可以的,因为加上volatile后, instance = new Singleton(); 则VM一定是有序执行指定,即先实例化后赋值,所以不会出现以前先赋值后实例化的问题
另外,如果程序中使用了 synchronized 键字作用于了共享资源,则不再需要volatile来修饰共享变量,因为synchronized 具有了原子性,可见性、有序性。
正如你所说的用ThreadLocal能解决这个问题,担是我认为你的代码里面的写法是不是有问题。
public class Singleton {
private static final ThreadLocal perThreadInstance = new ThreadLocal();
private static Singleton singleton ;
private Singleton() {}
public static Singleton getInstance() {
if (perThreadInstance.get() == null){
// 每个线程第一次都会调用
createInstance();
}
return singleton;
}
private static final void createInstance() {
synchronized (Singleton.class) {
if (singleton == null){
singleton = new Singleton();
}
}
perThreadInstance.set(perThreadInstance);
}
}
倒数第三行perThreadInstance.set(perThreadInstance);应该写成perThreadInstance.set(singleton);
除此之外每一个线程都会有一个singleton的副本这样一样会造成资源浪费。本来单态模式就是想节省资源的,这样与模式的初衷不相符吧。
还有这种ThreadLocal方式的解决方案能不能应用在分布式情形,不同的JVM,ClassLoader?
写成你那样我想也没有问题的,因为这里的ThreadLocal最主要的作用就是一个标识,它只是用来标示当前线程是否已经请求或使用过singleton实例,至于perThreadInstance.set(XX)成什么值我想并不重要,关键要只要访问过或使用过就将其值设成非null。
你所说的资源问题在这里不会有问题,不管是我的那种写法还是你建义的那种,都不会有资源问题,因为这里的键是共享的,不同的线程都会使用同一个perThreadInstance或你的singleton放入每个线程的ThreadLocalMap里面。
至于分布式问题,我真还没有考虑过,我想如果这种实现不能,那么其他方式也是有问题。
最后不同的ClassLoader肯定会有不同的singleton实例,这个可以从上面的测试可以看出来。
<div class="quote_div">
<p> </p>
<p> 坦白的说这样真不行,因为编译通过不了,你把返回语句放在if里面,后面你还得处理返回问题。其实说白了我刚才所说的情况也只是有可能发生的。如果在释放锁之前完成对像的初始化就不会有什么问题。担是我说的那种情况也是有可能发生的,线程的问题并不是每一次测试都能发现的,至于什么时候开始初始化的工作我想只有JVM才知道吧。</p>
<p> </p>
<p>简单概括你的问题,如果初始化发生在释放锁之前不会有什么问题,如果初始化发生在释放锁之后就有可能有问题。</p>
</div>
<p> </p>
<p>编译是不会有问题的,因为我现在的项目就用到了第三种方式,我按上面那样该,没有问题</p>
<div class="quote_div">
<pre name="code" class="java">public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {// 1
if (instance == null) {// 2
instance = new Singleton();// 3
//在这里直接返回
return instance;
}
}
}
return instance;
}
}</pre>
</div>
<p>我认为这样一样还有可能会有问题,此时返回的instance也是有可能没有初始化的。</p>
<p> </p>
public class Singleton { private volatile static Singleton instance = null; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) {// 1 if (instance == null) {// 2 instance = new Singleton();// 3 //在这里直接返回 return instance; } } } return instance; } }
<div class="quote_div">我理解你的意思,那改成这样: <br><pre name="code" class="java"> if (instance == null) { //0
synchronized (Singleton.class) {// 1
if (instance == null) {// 2
instance = new Singleton();// 3
//这里是不是只有instance初始化完整,他才会返回?
return instance;// 4
}
}</pre>
<br><br>是不是就能解决问题?</div>
<p> </p>
<p> 坦白的说这样真不行,因为编译通过不了,你把返回语句放在if里面,后面你还得处理返回问题。其实说白了我刚才所说的情况也只是有可能发生的。如果在释放锁之前完成对像的初始化就不会有什么问题。担是我说的那种情况也是有可能发生的,线程的问题并不是每一次测试都能发现的,至于什么时候开始初始化的工作我想只有JVM才知道吧。</p>
<p> </p>
<p>简单概括你的问题,如果初始化发生在释放锁之前不会有什么问题,如果初始化发生在释放锁之后就有可能有问题。</p>
if (instance == null) { //0 synchronized (Singleton.class) {// 1 if (instance == null) {// 2 instance = new Singleton();// 3 //这里是不是只有instance初始化完整,他才会返回? return instance;// 4 } }
是不是就能解决问题?
<div class="quote_div">
<div class="quote_title">dominic6988 写道</div>
<div class="quote_div">
<div class="quote_title">fengsky491 写道</div>
<div class="quote_div">我就想问下,为什么第三种方式,//3没执行完(也就是没有new完整),它为什么会释放锁?</div>
<p> </p>
<p> 第三步可以看作一个赋值语句,只不过是调用构造函数初始化在付值语句之后。另外一个线程得到锁后就看到当前的instence已经不是null了就直接返回了,这个时候有可能第一个线程初始化工作做了一半,或者没有做。这样后面的线程得到的对像就会有问题。我感觉是这样的。</p>
</div>
<p> </p>
<p>还有new的时候它为什么不会初始化完整了才,释放锁?</p>
<p> </p>
<p>照这样理解,我是不是也可以认为第二种,在new的时候,只初始化一半,就释放了锁,其他线程进来不是一样看到的instance也不是null,而照样返回一个不完整的实例?</p>
</div>
<pre name="code" class="java">public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) { //0
synchronized (Singleton.class) {// 1
if (instance == null) {// 2
instance = new Singleton();// 3
}
} //4
}
return instance;
}
}
</pre>
<p> 看上面的代码如果线程一执行语句3因为JVM编译器的优化工作会在构造方法实例化对像之前从构造方法返回指向该对像的引用。此时并没有执行构造方法。</p>
<p>然后程序执行出4此时开始执行构造方法当执行到一半的时候线程的时间片到期,此时并没有完成,线程二在0处结束等待并开始执行发现instance不为空就返回了。</p>
<p>我这样说你能理解吗?</p>
<div class="quote_div">
<div class="quote_title">fengsky491 写道</div>
<div class="quote_div">我就想问下,为什么第三种方式,//3没执行完(也就是没有new完整),它为什么会释放锁?</div>
<p> </p>
<p> 第三步可以看作一个赋值语句,只不过是调用构造函数初始化在付值语句之后。另外一个线程得到锁后就看到当前的instence已经不是null了就直接返回了,这个时候有可能第一个线程初始化工作做了一半,或者没有做。这样后面的线程得到的对像就会有问题。我感觉是这样的。</p>
</div>
<p> </p>
<p>还有new的时候它为什么不会初始化完整了才,释放锁?</p>
<p> </p>
<p>照这样理解,我是不是也可以认为第二种,在new的时候,只初始化一半,就释放了锁,其他线程进来不是一样看到的instance也不是null,而照样返回一个不完整的实例?</p>
总之楼主总结的不错。能用的这几种方式都总结出来了。
是因为这样
<div class="quote_div">我就想问下,为什么第三种方式,//3没执行完(也就是没有new完整),它为什么会释放锁?</div>
<p> </p>
<p> 第三步可以看作一个赋值语句,只不过是调用构造函数初始化在付值语句之后。另外一个线程得到锁后就看到当前的instence已经不是null了就直接返回了,这个时候有可能第一个线程初始化工作做了一半,或者没有做。这样后面的线程得到的对像就会有问题。我感觉是这样的。</p>
这样的写法是能避免无序写入的问题。因为别的线程进入不了方法体,除非当前线程释放锁。这样就能确保实例化完成。我的理解对吗?
public class Singleton {
private volatile static Singleton instance = null;
private Singleton() {}
public static Singleton getInstance() {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
}
正如你所说的用ThreadLocal能解决这个问题,担是我认为你的代码里面的写法是不是有问题。
public class Singleton {
private static final ThreadLocal perThreadInstance = new ThreadLocal();
private static Singleton singleton ;
private Singleton() {}
public static Singleton getInstance() {
if (perThreadInstance.get() == null){
// 每个线程第一次都会调用
createInstance();
}
return singleton;
}
private static final void createInstance() {
synchronized (Singleton.class) {
if (singleton == null){
singleton = new Singleton();
}
}
perThreadInstance.set(perThreadInstance);
}
}
倒数第三行perThreadInstance.set(perThreadInstance);应该写成perThreadInstance.set(singleton);
除此之外每一个线程都会有一个singleton的副本这样一样会造成资源浪费。本来单态模式就是想节省资源的,这样与模式的初衷不相符吧。
还有这种ThreadLocal方式的解决方案能不能应用在分布式情形,不同的JVM,ClassLoader?
我认为第二种方法同步方法加锁对像是this,而第三种加载方式是同步当前类的类对像。所以单从范围上面来讲只是if (instance == null)这一句的区别。
第二种加锁的对象不是this,其实也是 Singleton.class 锁对象,以前我测试过。为什么说是Singleton.class而不是this呢,最简单的理由就是该方法是静态的,这就很明显了:静态方法是不能访问this的。
其实第二也等同于下在面:
public class Singleton { private volatile static Singleton instance = null; private Singleton() {} public static Singleton getInstance() { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } return instance; } }
第二种与第三种唯一的区别就是第三种多了“instance == null”的条件,但该条件的检测不是放在同步块中的,正是因为这一点,导致了双重检测失效!
我认为第二种方法同步方法加锁对像是this,而第三种加载方式是同步当前类的类对像。所以单从范围上面来讲只是if (instance == null)这一句的区别。
相关推荐
### 单例模式详解 #### 一、单例模式简介 单例模式(Singleton Pattern)是一种常用的软件设计模式,属于创建型模式之一。其目的是确保某个类只有一个实例,并提供一个全局访问点。单例模式的核心在于确保在系统...
它与静态类的区别在于,静态类不支持实例化,而单例模式可以通过`getInstance()`方法获取单个实例。静态类通常用于提供工具方法或全局变量,而单例模式则用于管理共享资源。 替代单例模式的解决方案可以考虑依赖...
### Java多线程单例模式详解 #### 一、单例模式概述 单例模式(Singleton Pattern)是一种常用的软件设计模式,它确保一个类仅有一个实例,并提供一个全局访问点。这种模式通常用于那些需要频繁实例化然后销毁的...
综合以上,这个项目可能是一个实现Android应用与服务器通信的示例,利用线程池优化后台任务执行,采用单例模式管理网络请求,并通过WebService接口进行数据交换。理解并熟练掌握这些技术,对于提升Android开发能力...
单例模式中有区分了懒汉式和饿汉式,懒汉式主要是用时间来换空间,饿汉式则是用空间来换时间。饿汉式是线程安全的,懒汉式是非线程安全的,如果要实现懒汉式的非线程安全,则可以再访问点添加synchronized关键字声明...
单例模式的实现方式有很多,比如饿汉式、懒汉式以及双重检查锁定(Double-Check Locking)等。在Windows Forms应用中,我们可能会希望主窗口只打开一次,这就是单例窗口的用武之地。 在描述中提到的"实现单例窗口,...
3. **双重检测锁模式** - **定义**:改进版的懒汉式单例模式,通过双重检查锁定来提高效率。 - **实现**:首先在静态方法中检查实例是否已经被创建,如果未创建,则同步该方法并再次检查实例是否存在,不存在则...
3. 双重检测锁式:结合了饿汉式的延迟加载和懒汉式的线程安全,但在某些JVM环境下可能会出现问题,不推荐使用。 4. 静态内部类式:利用类加载机制保证线程安全,延迟加载,效率较高。 5. 枚举单例:最安全、简洁的...
实验中可以实现饿汉式单例和双重检测锁单例两种方式。 通过以上实验,你可以深入理解每种创建型设计模式的核心思想,掌握其结构、优缺点以及适用场景,并能够使用Java编程语言实现这些模式。这不仅提升了编程技能,...
3. **双重检测锁式**:双重检查锁定,线程安全,可以实现延迟加载,但偶尔会因为JVM底层内部模型原因出现问题。 4. **静态内部类式**:静态内部类加载时初始化对象,线程安全,可以实现延迟加载,调用效率高。 5. ...
常见的线程安全单例模式有双重检查锁定(DCL)、静态内部类和枚举方式。 8. 线程通信:wait()、notify()和notifyAll()是Java Object类提供的方法,用于线程间的通信,配合synchronized使用,可以使线程在特定条件下...
双重检测锁式(由于 JVM 底层内部模型原因, 偶尔会出问题, 不建议使用) 静态内部类式(线程安全, 调用效率高, 但是可以延时加载) 枚举单例(线程安全, 调用效率高, 不能延时加载, 并且天然地防止反
3. **单例模式**:单例模式确保一个类只有一个实例,并提供全局访问点。在多线程环境中,实现线程安全的单例模式尤为重要,防止多个线程同时创建单例对象,保证了资源的有效利用和一致性。常见的实现方式有双重检查...
- **单例模式(Singleton Pattern)**:在游戏管理器(GameManager)或游戏控制器(GameController)中,可能需要确保只有一个实例存在,以全局管理游戏状态和资源。可以使用饿汉式(静态常量引用单例)、懒汉式...
笔记中列出了饿汉式、懒汉式、双重检测锁式、静态内部类和枚举类实现单例的方法,并探讨了防止反射和反序列化破解单例的方法。 2. 工厂模式(Factory Pattern):包括简单工厂模式、工厂方法模式和抽象工厂模式,...
在这个实验中,宋行健同学深入研究了创建型模式,包括原型模式和单例模式,并对碰撞检测进行了优化,同时提出了一些改进方案。 1. **原型模式**:在Coin类中应用了原型模式,以实现对象的深复制。GameManager类中,...
实现单例模式有多种方式,如懒汉式(lazy initialization)、饿汉式(eager initialization)、双重检查锁定(double-checked locking)以及静态内部类方式。每种方式都有其优缺点,例如懒汉式在多线程环境下可能不...