单例模式,他确保了一个类在指定时间内只有一个实例。这个实例充当一个共享资源的看门人或者作为一个中央通信枢纽。一个应用程序不能创建新的实例,所有的方法都通过改单一的实例来访问。应用程序通过这个类对外暴露的一个静态方法来调用改单例。
核心的系统功能通常使用单例模式来访问。例如,在Java中。java.lang.Runtime类就使用单例模式与应用程序的运行环境进行交互。
为什么单例模式由于使用一组静态方法?
1.继承与接口。 单例是对象,他可以基类继承并实现接口。
2.可能的多样性。 你可以改变你的想法,创建对个对象(例如一个线程一个)同时不需要修改大量的代码.。(当然,如果你这样做的话,那就不再是单例了)
3.动态绑定。 真正用来创建单例的类可以不再编译时决定,而在运行时决定。
单例模式并没有不足之处。由于在多线程环境中必须对该方法进行同步,所以减慢了对单例的访问。一个单例可能拖慢应用程序的启动时间,应为他需要初始化,并且可能会一直持有不再需要的资源。
问题:
你的应用程序使用一个日志类将调试信息写到控制台。怎样使用单例模式实现这个日志工具?
public class Logger {
//单例模式要求任何时候最多只有一个日志类的实例。最简单的方法是将构造函数设为私有的,并在类中初始化一个实例。
private static final Logger instance = new Logger();
private Logger(){};
public Logger getInstance(){
return instance;
}
public void log(String msg){
System.out.println(System.currentTimeMillis() + ":" + msg);
}
}
问题:
你的应用程序使用了一个单例,但是并不是总是必须的,她的初始化开销很大,如何改进?
单例模式并没有指定对象实例在何时创建,只限制最多只有类的一个实例。类在加载的时候就创建实例不是必须的,只需要在之前创建即可。通过这种方式,如果getInstance应该在返回他之前没有初始化,则进行初始化。这个技巧称为延迟初始化或延迟加载。
public class Logger {
private static Logger instance = null;
private Logger(){};
public static Logger getInstance(){
if(instance == null){
instance = new Logger();
}
return instance;
}
public void log(String msg){
System.out.println(System.currentTimeMillis() + ":" + msg);
}
}
这个简单的改变完成了初始化延迟,但是他不在是线程安全的。如果两个线程同时尝试着去调用创建getInstance(),这时就会发生问题。
可以添加synchronized关键字
public synchronized static Logger getInstance(){
if(instance == null){
instance = new Logger();
}
return instance;
}
由于同步,这个改变需要付出巨大的性能代价,
java中的一个用法组合了延迟初始化和静态初始化,通过利用内部类的延迟加载,对实例进行静态初始化,这是线程安全的,因为类加载器保证是串行的,,无论多少线程同时调用getInstance,所以内部类只被加载和初始化一次,也避免了同步开销,因为串行化由类加载器保证--当类被加载后,就不再涉及类加载器,就没有剩余开销。可以将前面的getInstance通过如下替换来实现:
private static class LoggerHolder{
public static final Logger instance = new Logger();
}
public static Logger getInstance(){
return LoggerHolder.instance;
}
--参考于《程序员面试攻略》