1.单例模式有什么用处?
有一些对象只能使用一个,例如:数据库连接、线程池(threadpool)、缓存(cache)、对话框、处理偏好(preferences)设置和这侧表(registry)的对象、日志对象、充当打印机、显卡等设备的驱动程序的对象,即用于管理共享的资源。这种对象只能有一个实例,制造多个会导致问题。
2.最经典的单例模式?/**
* 1.利用一个私有静态变量来记录Singleton类的唯一实例;
* 2.构造器声明为私有,只有Singleton类本身内部才可调用;
* 3.声明共有静态的方法getInstance()来实例化对象,并返回这个对象的实例;
* 4.说明:如果我们不需要这个实例,就不会调用getInstance()方法,那么这个实例就不会产生,
* 称为“延迟实例化(lazy instantiaze)”。这种做法对资源敏感的对象特别重要。
*/
public class Singleton {
private static Singleton sInstance;
private Singleton(){}
public static Singleton getInstance(){
if(sInstance == null){
sInstance = new Singleton();
}
return sInstance;
}
}
3.单例模式定义--确保一个类只有一个实例,并提供一个全局访问点。
(1)全局访问点即向外提供的获取唯一实例的唯一入口,是共有静态的,访问它对比访问一般的全局变量的优点在于:单例中的全局访问点可以做到“延迟实例化”。
(2)此时的单例可能遇到的麻烦--多线程
正如下图中情况所示,当有多个线程同时访问该访问点时,以两个线程为例,就会出现可能实例化两个对象,这对于使用单例的地方可能引发灾难呀...
4.怎样改善多线程情况下的单例?
(1)不推荐的方法--synchronized
同步getInstance()方法既简单又有效,但是可能造成程序执行效率下降100倍,如果getInstance()被调用的频繁,只能重新考虑其他方法了;
(2)什么都不做
没错,什么都不做。如果应用程序可以接受getInstance()造成的额外负担,那就完全可以忘记这件事;
(3)使用“急切实例化(eagerly)”而非“延迟实例化”的做法
a.如果应用程序总是需要创建使用单例实例;
b.如果应用程序在创建运行时方面的负担不太重;
此时可选择“急切实例化”:public class EagerlySingleton {
/**在静态初始化器中创建单例实例,保证了线程安全**/
private static EagerlySingleton eSingletonInstance = new EagerlySingleton();
private EagerlySingleton(){};
public static EagerlySingleton getInstance(){
return eSingletonInstance;
}
}
这个做法,依赖JVM在加载这个类时就创建这个唯一的单例实例,保证了任何线程在访问这个静态变量之前,该实例已经创建。
(4)用“双重检查加锁(double checked locking)”,在getInstance()中减少使用同步/**
* 1.volatile确保实例化时,多个线程能正确的处理sInstance;
* 2.判断sInstance == null后在使用synchronized,且synchronized后在判断一次sInstance == null
* 3.双重检查加锁不适用于1.4以及更早的版本的Java。
*/
public class DoubleCheckSingleton {
private volatile static DoubleCheckSingleton sInstance;
private DoubleCheckSingleton(){}
public static DoubleCheckSingleton getInstance(){
if(sInstance == null){
synchronized (DoubleCheckSingleton.class) {
if(sInstance == null){
sInstance = new DoubleCheckSingleton();
}
}
}
return sInstance;
}
}