单例模式(Singleton),保证一个类仅仅有一个实例,并且提供一个访问它的全局访问点。
通常我们可以让一个全局变量使其被一个对象访问,但是它不能防止实例化多个对象。最好的方法就是,让类自身负责保存它的唯一实例,同时这个类保证没有其他实例可以被创建,并且它提供了一个访问该实例的方法。
模式构成要素:
1、适用场景:
1.需要生成唯一序列的环境。
2.需要频繁实例化然后销毁的对象。
3.创建对象时耗时过多或者耗资源过多,但又经常用到的对象。
4.方便资源相互通信的环境。
2、场景举例
以下是单例模式的经典使用场景:
1.资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。
2.控制资源的情况下,方便资源之间的互相通信。如线程池等。
其他应用场景举例:
1.外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件
2. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~
3. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。
4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。
5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。
7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。
8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。
9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。
10. HttpApplication 也是单位例的典型应用。熟悉ASP.Net(IIS)的整个请求生命周期的人应该知道HttpApplication也是单例模式,所有的HttpModule都共享一个HttpApplication实例.
1.饿汉式:单例实例在类装载时就构建,急切初始化。(预先加载法)
public class Singleton {
private Singleton() {
}
public static Singleton instance = new Singleton();
public Singleton getInstance() {
return instance;
}
}
优点 :
缺点:
另外一种实现,静态内部类
public class Singleton {
private Singleton() {
}
private static class SingletonBuilder {
static Singleton instance = new Singleton();
}
public static Singleton getInstance() {
return SingletonBuilder.instance;
}
}
/*
优点:
资源利用率高,不执行getInstance()不被实例,可以执行该类其他静态方法
缺点:
第一次加载时反应不够快*/
2.懒汉式:单例实例在第一次被使用时构建,延迟初始化。
public class Singleton {
private static Singleton instance;
//构造器私有化
private Singleton() {
}
/**
* 若实例存在返回实例,否则创建实例
* @return instance实例
*/
public static Singleton getInstance(){
if(instance ==null){
instance =new Singleton();
}
return instance;
}
}
优点:
避免了饿汉式的那种在没有用到的情况下创建事例,资源利用率高,不执行getInstance()就不会被实例,可以执行该类的其他静态方法。
缺点:
懒汉式在单个线程中没有问题,但多个线程同事访问的时候就可能同事创建多个实例,而且这多个实例不是同一个对象,虽然后面创建的实例会覆盖先创建的实例,但是还是会存在拿到不同对象的情况。解决这个问题的办法就是加锁synchonized,第一次加载时不够快,多线程使用不必要的同步开销大。
3、多线程时的单例模式
public class Singleton {
private static Singleton instance;
//构造器私有化
private Singleton() {
}
public static Singleton getInstance() {
//临界区互斥访问
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
}
这样有个很大的问题,每次调用GetInstance方法都需要lock,很大地影响性能,因此需要改良,所以有了双重锁定(Double-Check-Lock)。
4、双重锁定(Double-Check-Lock)
public class Singleton {
private static Singleton instance;
//构造器私有化
private Singleton() {
}
public static Singleton getInstance() {
//临界区互斥访问
//先判断实例是否存在,不存在再进行加锁处理
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
//检查该单例是否已经被创建,保证单例模式
instance = new Singleton();
}
}
}
return instance;
}
}
优点 :资源利用率高,不执行getInstance()就不被实例,可以执行该类其他静态方法 。
缺点 :第一次加载时反应不快,由于java内存模型一些原因偶尔失败 。
1、java.awt.Toolkit
ToolKit 是一个抽象类,ToolKit 作为 AWT 工具箱,提供了 GUI 最底层的 Java 访问,例如从系统获取图像、获取屏幕分辨率,获取屏幕色彩模型、全屏的时候获得屏幕大小等。很多时候并不常用,适合使用懒汉模式,使用饿汉反而会导致虚拟机JVM启动过慢。
2、Runtime类是使用的单例设计模式。
采用饿汉模式,提高虚拟机加载速度。
总结:
一般采用饿汉式,若对资源十分在意可以采用静态内部类,不建议采用懒汉式及双重检测