深入解析单例模式

来源:互联网 发布:山西省软件开发企业 编辑:程序博客网 时间:2024/05/28 14:57

一.单例模式的定义

单例模式(Singleton Pattern):确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例,这个类称为单例类,它提供全局访问的方法。单例模式是一种对象创建型模式。

二.单例模式的应用场景举例

1. 在windows下的任务管理器实现。

2.编写Web应用时配置对象的读取。

3.网页的计数器。

4.Java中的线程池集合类的设计。

在应用需要对象实现某一功能, 又要避免大规模创建对象造成资源浪费时,可以考虑使用单例模式。其次,在应用需要控制某一资源,使全局可以访问时可以考虑单例模式。

三.单例模式的实现方法

1.饿汉式单例模式

public class Singleton {private static final Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance(){return instance;}}

在类加载的时候加载单例对象,使用这种方法的优点是在多线程的情况下只有一个实例对象,可以保证单例对象的唯一性,并且在使用的时候可以使应用更流畅(因为之前已经进行过加载);缺点是如果该对象得不到使用那么该单例模式创建的对象也会一直驻留在内存中。

2.懒汉式单例模式

public class Singleton {private static Singleton instance = null;private Singleton() {}public static Singleton getInstance(){if (instance == null){instance = new Singleton();}return instance;}}
在多线程并发访问的时候,上述实现是不安全的。假设线程A刚刚执行完判断instance是否为空的if语句,时间片段分配给线程B,线程B判断instance为空,new出了一个单例对象,然后切换回线程A执行,由于线程A执行过判断的语句,顺序执行将再次创建单例对象。违背了单例模式的设计原则。

public class Singleton {private static Singleton instance = null;private Singleton() {}public static synchronized Singleton getInstance(){if (instance == null){instance = new Singleton();}return instance;}}

在获取单例对象的getInstance()方法前简单增加synchronized关键字使得在多线程的情况下不会出现多个单例对象,Java的线程池集合也是使用的这种方法来实现的。但是在高并发的情况下每次调用方法时都需要加锁使得系统性能大大降低。

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;}}

只在创建单例对象的时候加锁可以缓解上述的问题。由于不需要每次调用getInstance()方法时都加锁,而是只在创建时加锁使得性能有很大的提高。但是这段代码真的能保证在多线程的情况下是安全的么(在系统中只有一个单例对象)?和前面的分析类似,如果线程A在执行过if语句的判断之后系统将时间片段交给线程B,那么系统中仍然会出现两个单例对象。

public class Singleton {private static volatile Singleton instance = null;private Singleton() {}public static Singleton getInstance(){if (instance == null){synchronized(Singleton.class){if(instance == null){instance = new Singleton();}}}return instance;}}

在synchronized块内加上对instance是否为空的判断可以解决上述问题,同时,需要将单例对象声明为volatile的,这样做的目的是可以屏蔽虚拟机的一些优化,但是性能上会有一定降低。

通过上面的代码可见不论是饿汉式还是懒汉式都有一定的缺点,有没有一种方法可以实现延时加载并且还不用考虑由多线程问题带来的性能损耗呢?有的

public class Singleton {private Singleton() {}private static class Holder {private final static Singleton instance = new Singleton();}public static Singleton getInstance() {return Holder.instance;}}

在单例类中没有静态类Holder的对象,所以在类加载的时候并不会创建单例对象。当调用getInstance()方法时,Holder类进行加载并且创建出单例对象。高效的实现了上述的需求。

在实际的需求中可以根据自己应用的情况来选取单例模式的使用。

四.单例模式的缺点

毕竟不存在完美的模式,单例模式存在着以下几点不足之处:

1.单例模式违背了“单一职责原则”,在单例类的内部有创建实例的代码,也有执行功能逻辑的代码。有可能使单例类过于庞大和冗杂,不利于今后的扩展。

2.在java中存在着垃圾回收机制,在内存不足的情况下单例对象也有可能被系统回收,那么单例对象内的数据域也就会消失。如果有担心这个情况发生可以将重要的数据域存储到本地文件或者数据库中,在单例对象被回收后可以恢复数据。





0 0