- 浏览: 195972 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
zoumeizhong123:
very good,
VBA制作Excel报表 -
jiht594:
jiht594 写道楼主你好:这个第一段js代码+那一行htm ...
Ext学习笔记05 - UI组件 - Panel,TextField -
jiht594:
楼主你好:这个第一段js代码+那一行html代码我试的时候为什 ...
Ext学习笔记05 - UI组件 - Panel,TextField -
zx_921:
多谢 还有别的方法 例如取消linksbuilder 不 ...
Errors running builder "linksbuilder", NullPointException 问题 -
piaoxue_x:
嗯,写的不错,明白啥意思了。。。。。。就是和配置助手里面配的那 ...
DB2 - Catalog
面试被问到一个线程安全的单例模式问题,想拿出来讨论一下,
我通常会使用的这样的写法来实现单例:
public class Singleton { private Singleton() {} private static Singleton instance = null; public static Singleton getInstance() { if(instance == null) { instance = new Singleton(); } return instance; } }
单例的目的是为了保证运行时Singleton类只有唯一的一个实例,最常用的地方比如拿到数据库的连接,Spring的中创建BeanFactory这些开销比较大的操作,而这些操作都是调用他们的方法来执行某个特定的动作。
面试官的问题是:单例会带来什么问题?
我第一反映就是如果多个线程同时调用这个实例,会有线程安全的问题,当时就这么说了,然后他问:“怎么实现一个线程安全的单例模式呢?”
这个问题我没有回答上来,当时脑子里闪了一下如果用synchronized来锁定可能会有一些问题,至于是什么问题没有想明白,就选择没有回答。
这里请问各位高手,
1、如果不执行修改对象的操作的情况下,单单执行一个读取操作,还有没有进行同步的必要?
2、保证单例的线程安全使用synchronized会产生什么样的问题?
3、不使用synchronized,有什么方式来保证线程安全?
4、假如下次再面试遇到这种情形,用什么方式回答会使面试官感到比较满意?
--------------------------------------------------------------------------------------------------------------------------------------------------------------
感谢大家的讨论与支持,总结一下:
实际上使用什么样的单例实现取决于不同的生产环境,懒汉式也就是我在上面举得那个例子,这种方式适合于单线程程序,多线程情况下需要保护getInstance()方法,否则可能会产生多个Singleton对象的实例。
在此基础上确保getInstance()方法一次只能被一个线程调用就需要在getInstance()方法之前加上 synchronized 关键字,锁定整个方法,
public class Singleton{ private static Singleton instance=null; private Singleton(){} public static synchronized Singleton getInstance(){ if(instance==null){ instance=new Singleton(); } return instance; } }
但很多时候我们通常会认为锁定整个方法的是比较耗费资源的,代码中实际会产生多线程访问问题的只有 instance = new Singleton(); 这一句,
为了降低 synchronized 块性能方面的影响,只锁定instance = new Singleton(); 这一句,“weishuang”回帖中使用的就是这种方式:
public class Singleton{ private static Singleton instance=null; private Singleton(){} public static Singleton getInstance(){ if(instance==null){ synchronized(Singleton.class){ instance=new Singleton(); } } return instance; } }
分析这种实现方式,两个线程可以并发地进入第一次判断instance是否为空的if 语句内部,第一个线程执行new操作,第二个线程阻断,当第一个线程执行完毕之后,第二个线程没有进行判断就直接进行new操作,所以这样做也并不是安全的。
为了避免第二次进入synchronized块没有进行非空判断的情况发生,添加第二次条件判断,就像“tomorrow009”在帖子中回复的示例一样
public static Singleton getInstance(){ if(instance == null){ synchronize{ if(instance == null){ instance = new Singleton(); } } } return instance; }
这样就产生了二次检查,但是二次检查自身会存在比较隐蔽的问题,查了Peter Haggar在DeveloperWorks上的一篇文章,对二次检查的解释非常的详细:
其实找到这篇文章之后,我的问题基本上就已经可以解决了,但是看到回帖的同学们也有一些和我一样的问题,还想把这个问题继续梳理一遍。
使用二次检查的方法也不是完全安全的,原因是 java 平台内存模型中允许所谓的“无序写入”会导致二次检查失败,所以使用二次检查的想法也行不通了。
Peter Haggar在最后提出这样的观点:“无论以何种形式,都不应使用双重检查锁定,因为您不能保证它在任何 JVM 实现上都能顺利运行。”
"netrice"在回复中提到了使用“java5以后的volatile关键字”,用volatile关键字来声明变量,声明成 volatile 的变量被认为是顺序一致的,即,不是重新排序的。但是volatile关键字的特性并不适用于这篇帖子所讨论的问题关键。
通过上面的分析,可以看到使用懒汉式的lazy方式实现单例弯弯绕太多,在单线程编程的情况下懒汉式单例实现是没有任何问题的,如果在多线程的情况下,我们需要比较小心,对getInstances()方法加上synchronized关键字,这样虽然可能有一些性能上的牺牲,但是更加的安全。绕了这么大的一个弯,又回来了:
/* 安全的方式 1 */ public class Singleton{ private static Singleton instance=null; private Singleton(){} public static synchronized Singleton getInstance(){ if(instance==null){ instance=new Singleton(); } return instance; } }
Peter Haggar提到的另外一种实现方式是这样的,放弃使用 synchronized 关键字,而使用 static 关键字:
/* 安全的方式 2 */ public class Singleton { private static Singleton instance = new Singleton(); private Singleton() {} public static Singleton getInstance() { return instance; } }
这种方式没有使用同步,并且确保了调用static getInstance()方法时才创建Singleton的引用(static 的成员变量在一个类中只有一份)。
还有“keshin”提到的方式则更加灵巧,没有使用同步但保证了只有一个实例,还同时具有了Lazy的特性(出自Lazy Loading Singletons)
/* 安全的方式 3 */ public class ResourceFactory { private static class ResourceHolder { public static Resource resource = new Resource(); } public static Resource getResource() { return ResourceFactory.ResourceHolder.resource; } static class Resource { } }
上面的方式是值得借鉴的,在ResourceFactory中加入了一个私有静态内部类ResourceHolder ,对外提供的接口是 getResource()方法,也就是只有在ResourceFactory .getResource()的时候,Resource对象才会被创建,
这种写法的巧妙之处在于ResourceFactory 在使用的时候ResourceHolder 会被初始化,但是ResourceHolder 里面的resource并没有被创建,
这里隐含了一个是static关键字的用法,使用static关键字修饰的变量只有在第一次使用的时候才会被初始化,而且一个类里面static的成员变量只会有一份,这样就保证了无论多少个线程同时访问,所拿到的Resource对象都是同一个。
饿汉式的实现方式虽然貌似开销比较大,但是不会出现线程安全的问题,也是解决线程安全的单例实现的有效方式。
至于ThreadLocal,我认为还是应该由使用场景来决定。
在《Java与模式》中,作者提出:“饿汉式单例类可以在Java语言实现,但不易在C++内实现,因为静态初始化在C++里没有固定的顺序,因而静态的instance变量的初始化与类的加载顺序没有保证,可能会出问题。这就是为什么GoF在提出单例类的概念时,举的例子是懒汉式的。他们的书影响之大,以致Java语言中单例类的例子也大多是懒汉式的。实际上,本书认为饿汉式单例类更符合Java语言本身的特点。”
由此可见在应用设计模式的同时,分析具体的使用场景来选择合适的实现方式是非常必要的。
寻找问题解决过程中找的一些参考资料:
因为在精华帖中没有找到很流畅解释这个问题的内容才发了这个帖子,还是很不幸的被评为了新手帖,但如果下次有面试官问有关线程安全的单例模式问题,我想我知道该怎么回答了。
评论
public class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){ //私有化构造函数. }; public static Singleton getInstance(){ return instance; } }
由于实例在类加载时就已经创建,所以不存在线程同步问题,如果该Singleton初始化时不需要很大的开销(比如io操作/数据库连接)之类的,通常用这种办法就可以了.
某些情况下我们希望实例只有被第一次用到的时候才创建,那么这时候就使用“懒汉”模式,初学者通常会采用LZ的写法,正如面试官所说,这样会有线程同步问题,而导致多个实例被创建。如果在 getInstance()方法前面加上synchronize,又会大大降低系统性能。其实我们需要考虑的是,究竟要同步哪里? 我们只需要同步new Singleton()这个部分,保证只有一个实例被创建出来即可,而无须同步整个getInstance()方法,那么就可以考虑这样做
public static Singleton getInstance(){ if(instance == null){ synchronize{ if(instance == null){ instance = new Singleton(); } } } }
应该就是这样了。
1. public static final ThreadLocal session = new ThreadLocal();
2. public static Session currentSession() {
3. Session s = (Session)session.get();
4. //open a new session,if this session has none
5. if(s == null){
6. s = sessionFactory.openSession();
7. session.set(s);
8. }
return s;
9. }
我们逐行分析
1。 初始化一个ThreadLocal对象,ThreadLocal有三个成员方法 get()、set()、initialvalue()。
如果不初始化initialvalue,则initialvalue返回null。
3。session的get根据当前线程返回其对应的线程内部变量,也就是我们需要的net.sf.hibernate.Session(相当于对应每个数据库连接).多线程情况下共享数据库链接是不安全的。ThreadLocal保证了每个线程都有自己的s(数据库连接)。
5。如果是该线程初次访问,自然,s(数据库连接)会是null,接着创建一个Session,具体就是行6。
6。创建一个数据库连接实例 s
7。保存该数据库连接s到ThreadLocal中。
8。如果当前线程已经访问过数据库了,则从session中get()就可以获取该线程上次获取过的连接实例。
关于ThreadLocal,在Martin Fowler的《企业应用架构模式》早已经提到了,因为在每个方法上都声明传递“工作单元”或“标识映射”这种做法不太好,幸好Java中有个ThreadLocal可以方便我们在同一线程传递这些。
在我看到过的国外以前的Web应用的代码中,是在service里创建了Connection,然后在调用dao的方法时,把connection传递到dao的方法中。这样同样是线程安全的。总之ThreadLocal这种用法并不是突然就从地底下碰出来,每个技术都是小步的演化过来的,大伙原来写的代码也不至于线程不安全了。
关于使用ThreadLocal来实现单例,我感觉你这不是个合适的例子?
public class ResourceFactory { private static class ResourceHolder { public static Resource resource = new Resource(); } public static Resource getResource() { return ResourceFactory.ResourceHolder.resource; } static class Resource { } }
java concurrency in practice中建议的方式
这个好, 延迟初始化,线程安全,效率高(没有使用同步锁,而由类加载器保证) 简洁
public class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){ //私有化构造函数. }; public static Singleton getInstance(){ return instance; } }
由于实例在类加载时就已经创建,所以不存在线程同步问题,如果该Singleton初始化时不需要很大的开销(比如io操作/数据库连接)之类的,通常用这种办法就可以了.
某些情况下我们希望实例只有被第一次用到的时候才创建,那么这时候就使用“懒汉”模式
第一种方式(加载时创建实例)有什么问题?类的加载机制是第一次调用static方法时类才加载,也就是在调用getInstance()方法时Singleton才被加载,这样跟第二种的加载时间有什么不同?不都是第一次使用时才加载吗?
以上是我的困惑,希望楼下有人给我解惑
1. public static final ThreadLocal session = new ThreadLocal();
2. public static Session currentSession() {
3. Session s = (Session)session.get();
4. //open a new session,if this session has none
5. if(s == null){
6. s = sessionFactory.openSession();
7. session.set(s);
8. }
return s;
9. }
我们逐行分析
1。 初始化一个ThreadLocal对象,ThreadLocal有三个成员方法 get()、set()、initialvalue()。
如果不初始化initialvalue,则initialvalue返回null。
3。session的get根据当前线程返回其对应的线程内部变量,也就是我们需要的net.sf.hibernate.Session(相当于对应每个数据库连接).多线程情况下共享数据库链接是不安全的。ThreadLocal保证了每个线程都有自己的s(数据库连接)。
5。如果是该线程初次访问,自然,s(数据库连接)会是null,接着创建一个Session,具体就是行6。
6。创建一个数据库连接实例 s
7。保存该数据库连接s到ThreadLocal中。
8。如果当前线程已经访问过数据库了,则从session中get()就可以获取该线程上次获取过的连接实例。
这个才是正路~~
可算是知道个ThreadLocal了,拜托给个示例。
public class Singleton{ private static Singleton instance = new Singleton(); private Singleton(){ //私有化构造函数. }; public static Singleton getInstance(){ return instance; } }
由于实例在类加载时就已经创建,所以不存在线程同步问题,如果该Singleton初始化时不需要很大的开销(比如io操作/数据库连接)之类的,通常用这种办法就可以了.
某些情况下我们希望实例只有被第一次用到的时候才创建,那么这时候就使用“懒汉”模式,初学者通常会采用LZ的写法,正如面试官所说,这样会有线程同步问题,而导致多个实例被创建。如果在 getInstance()方法前面加上synchronize,又会大大降低系统性能。其实我们需要考虑的是,究竟要同步哪里? 我们只需要同步new Singleton()这个部分,保证只有一个实例被创建出来即可,而无须同步整个getInstance()方法,那么就可以考虑这样做
public static Singleton getInstance(){ if(instance == null){ synchronize{ if(instance == null){ instance = new Singleton(); } } } }
由于java语言的特性,双重检查锁是不行的,所以
看了它的实现,感觉只要在get的时候加上synchronized关键
字就可以了吧。
/* * The Apache Software License, Version 1.1 * * Copyright (c) 1999 The Apache Software Foundation. All rights * reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The end-user documentation included with the redistribution, if * any, must include the following acknowlegement: * "This product includes software developed by the * Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowlegement may appear in the software itself, * if and wherever such third-party acknowlegements normally appear. * * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software * Foundation" must not be used to endorse or promote products derived * from this software without prior written permission. For written * permission, please contact apache@apache.org. * * 5. Products derived from this software may not be called "Apache" * nor may "Apache" appear in their names without prior written * permission of the Apache Group. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Software Foundation. For more * information on the Apache Software Foundation, please see * <http://www.apache.org/>. * * [Additional notices, if required by prior licensing conditions] * */ package org.apache.catalina.util; import java.text.MessageFormat; import java.util.Hashtable; import java.util.MissingResourceException; import java.util.ResourceBundle; /** * An internationalization / localization helper class which reduces * the bother of handling ResourceBundles and takes care of the * common cases of message formating which otherwise require the * creation of Object arrays and such. * * <p>The StringManager operates on a package basis. One StringManager * per package can be created and accessed via the getManager method * call. * * <p>The StringManager will look for a ResourceBundle named by * the package name given plus the suffix of "LocalStrings". In * practice, this means that the localized information will be contained * in a LocalStrings.properties file located in the package * directory of the classpath. * * <p>Please see the documentation for java.util.ResourceBundle for * more information. * * @author James Duncan Davidson [duncan@eng.sun.com] * @author James Todd [gonzo@eng.sun.com] */ public class StringManager { /** * The ResourceBundle for this StringManager. */ private ResourceBundle bundle; /** * Creates a new StringManager for a given package. This is a * private method and all access to it is arbitrated by the * static getManager method call so that only one StringManager * per package will be created. * * @param packageName Name of package to create StringManager for. */ private StringManager(String packageName) { String bundleName = packageName + ".LocalStrings"; bundle = ResourceBundle.getBundle(bundleName); } /** * Get a string from the underlying resource bundle. * * @param key */ public String getString(String key) { if (key == null) { String msg = "key is null"; throw new NullPointerException(msg); } String str = null; try { str = bundle.getString(key); } catch (MissingResourceException mre) { str = "Cannot find message associated with key '" + key + "'"; } return str; } /** * Get a string from the underlying resource bundle and format * it with the given set of arguments. * * @param key * @param args */ public String getString(String key, Object[] args) { String iString = null; String value = getString(key); // this check for the runtime exception is some pre 1.1.6 // VM's don't do an automatic toString() on the passed in // objects and barf out try { // ensure the arguments are not null so pre 1.2 VM's don't barf Object nonNullArgs[] = args; for (int i=0; i<args.length; i++) { if (args[i] == null) { if (nonNullArgs==args) nonNullArgs=(Object[])args.clone(); nonNullArgs[i] = "null"; } } iString = MessageFormat.format(value, nonNullArgs); } catch (IllegalArgumentException iae) { StringBuffer buf = new StringBuffer(); buf.append(value); for (int i = 0; i < args.length; i++) { buf.append(" arg[" + i + "]=" + args[i]); } iString = buf.toString(); } return iString; } /** * Get a string from the underlying resource bundle and format it * with the given object argument. This argument can of course be * a String object. * * @param key * @param arg */ public String getString(String key, Object arg) { Object[] args = new Object[] {arg}; return getString(key, args); } /** * Get a string from the underlying resource bundle and format it * with the given object arguments. These arguments can of course * be String objects. * * @param key * @param arg1 * @param arg2 */ public String getString(String key, Object arg1, Object arg2) { Object[] args = new Object[] {arg1, arg2}; return getString(key, args); } /** * Get a string from the underlying resource bundle and format it * with the given object arguments. These arguments can of course * be String objects. * * @param key * @param arg1 * @param arg2 * @param arg3 */ public String getString(String key, Object arg1, Object arg2, Object arg3) { Object[] args = new Object[] {arg1, arg2, arg3}; return getString(key, args); } /** * Get a string from the underlying resource bundle and format it * with the given object arguments. These arguments can of course * be String objects. * * @param key * @param arg1 * @param arg2 * @param arg3 * @param arg4 */ public String getString(String key, Object arg1, Object arg2, Object arg3, Object arg4) { Object[] args = new Object[] {arg1, arg2, arg3, arg4}; return getString(key, args); } // -------------------------------------------------------------- // STATIC SUPPORT METHODS // -------------------------------------------------------------- private static Hashtable managers = new Hashtable(); /** * Get the StringManager for a particular package. If a manager for * a package already exists, it will be reused, else a new * StringManager will be created and returned. * * @param packageName */ public synchronized static StringManager getManager(String packageName) { StringManager mgr = (StringManager)managers.get(packageName); if (mgr == null) { mgr = new StringManager(packageName); managers.put(packageName, mgr); } return mgr; } }
这个才是正路~~
有何高见,很想听你分析分析。
java 不支持二次检查 生成单例 ,楼主的例子一次检查就更不用说了。
1. 单例会导致这个类永远都不会被GC,如果过度使用代理会导致停留在堆区的类超级多。所以说,单例也要慎用。
2. 写法,满天飞。饱汉,饿汉,holder,enum都能实现。都有自己的说法。
有何高见,很想听你分析分析。
public class ResourceFactory { private static class ResourceHolder { public static Resource resource = new Resource(); } public static Resource getResource() { return ResourceFactory.ResourceHolder.resource; } static class Resource { } }
java concurrency in practice中建议的方式
private static Singleton instance=null;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
instance=new Singleton();
}
}
return instance;
}
}
这种方法才是最好的方法,只需要同步一次,但是这种方法在JDK1.2的版本中会出现错误,会被当作垃圾给回收掉。
public class Singleton{
static class SingletonHolder{
static Singleton instance=new Singleton();
}
public static Singleton getInstance(){
return Singleton.instance();
}
}
最早从google的一个叫lee的人那里学来的,现在基本都这样写
相关推荐
- 面试题:如何实现线程间的同步和通信? 10. **反射与注解** - 解释反射机制及其在运行时动态操作类和对象的能力。 - 面试题:如何通过反射创建并调用一个类的方法? - 讨论注解的用途,如代码自动生成、元...
- 单例模式:饿汉式、懒汉式、双重检查锁定和静态内部类实现。 - 工厂模式:简单工厂、工厂方法和抽象工厂模式。 - 观察者模式和装饰者模式:在实际开发中的应用。 7. **Java 8及以后的新特性**: - Lambda...
Java作为一门广泛使用的编程语言,其面试题涵盖了众多的知识领域,包括基础语法、面向对象、集合框架、多线程、网络编程、IO流、异常处理、JVM内存模型、设计模式、数据库操作、Spring框架等。以下是对这些知识点的...
设计模式是解决软件开发中常见问题的最佳实践,面试题通常会考察以下设计原则和模式: 1. **设计原则**:单一职责原则、开闭原则、里氏替换原则、依赖倒置原则、接口隔离原则和迪米特法则。 2. **创建型模式**:单例...
【标题】:“NET面试题-多线程编程与线程同步1” 【描述】:本篇文章主要探讨.NET框架下的多线程编程与线程同步相关的面试问题,包括线程与进程的区别、GUI线程访问控件限制、后台线程与前台线程、锁的使用以及线程...
- 面试题:描述单例模式并给出实现方式。 - 答案:单例模式确保一个类只有一个实例,并提供全局访问点。常见的实现方式有饿汉式、懒汉式(线程不安全和线程安全)以及双重检查锁定。 10. **JVM内存模型** - 面试...
8. **设计模式**:常见的设计模式有单例模式、工厂模式、建造者模式、代理模式、装饰器模式、适配器模式、观察者模式、策略模式等,理解其应用场景和实现方式。 9. **JVM内存模型**:了解堆内存、栈内存、方法区、...
- 面试题:描述单例模式、工厂模式和装饰器模式的实现方式。 - 实战代码:实现常见设计模式,如单例、工厂、观察者、策略等。 9. **JVM优化**: - 面试题:JVM有哪些运行时区域?如何调整JVM参数以优化性能? -...
Java作为一门广泛使用的编程语言,其面试题涵盖了基础语法、面向对象、集合框架、多线程、网络编程、异常处理、JVM、设计模式、数据库交互、Spring框架等多个方面。以下是对这些知识点的详细解析: 1. **Java基础**...
23. **线程安全的初始化**:单例模式的双检锁(DCL)和静态内部类等实现方式。 24. **线程安全的日期时间类**:Java 8引入的`java.time`包中的类是线程安全的,替代了旧的`java.util.Date`和`Calendar`。 25. **...
操作系统面试题常常涵盖广泛的主题,包括但不限于进程管理、内存管理、线程同步、资源调度等。在这个场景中,我们关注的是“单例模式”以及与操作系统相关的“进程管理”。 单例模式是一种设计模式,用于确保一个类...
在面试中,关于工厂模式的问题可能包括:解释工厂模式的概念、比较不同类型的工厂模式、在实际项目中如何运用工厂模式、分析工厂模式的优缺点以及与其它设计模式(如建造者模式、单例模式)的区别等。候选人需要能够...
### 黑马面试题总结 #### 一、进程与线程状态 **知识点:** - **进程与线程的区别:** - **进程**:是系统进行资源分配和调度的基本单位,每个进程都有独立的代码和数据空间(程序上下文)。 - **线程**:是...
- 单例模式:保证一个类只有一个实例,并提供全局访问点。 - 工厂方法:定义一个创建对象的接口,让子类决定实例化哪一个类。 - 抽象工厂:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们的具体类...
- 在多线程环境下,必须确保单例模式的线程安全性。 - 使用枚举(Enum)实现单例模式是一种简单且高效的方式,从Java 5开始支持。 - 需要注意懒汉式单例模式可能会带来的性能开销。 - 避免在单例类中使用过多的状态...
设计模式面试题 设计模式是一套被反复使用、多数人知晓的、经过分类编目的、代码设计经验的总结。使用设计模式是为了可重用代码、让代码更容易被他人理解、保证代码可靠性、程序的重用性。 学习设计模式的必要性:...