单例模式(Singleton),也叫单子模式,是一种常用的软件设计模式。在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个的全局对象,这样有利于我们协调系统整体的行为。比如在某个服务器程序中,该服务器的配置信息存放在一个文件中,这些配置数据由一个单例对象统一读取,然后服务进程中的其他对象再通过这个单例对象获取这些配置信息。这种方式简化了在复杂环境下的配置管理。
一个类能返回对象一个引用(永远是同一个)和一个获得该实例的方法(必须是静态方法,通常使用getInstance这个名 称);当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用;同时我们 还将该类的构造函数定义为私有方法,这样其他处的代码就无法通过调用该类的构造函数来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
单例模式在多线程的 应用场合下必须小心使用。如果当唯一实例尚未创建时,有两个线程同时调用创建方法,那么它们同时没有检测到唯一实例的存在,从而同时各自创建了一个实例, 这样就有两个实例被构造出来,从而违反了单例模式中实例唯一的原则。 解决这个问题的办法是为指示类是否已经实例化的变量提供一个互斥锁(虽然这样会降低效率)。
单例模式只允许创建一个对象,因此节省内存,加快对象访问速度,因此对象需要被公用的场合适合使用,如多个模块使用同一个数据源连接对象等等。如:
以下都是单例模式的经典使用场景:
应用场景举例:
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.单例模式:确保一个类只有一个实例,自行实例化并向系统提供这个实例
2.单例模式分类:饿单例模式(类加载时实例化一个对象给自己的引用),懒单例模式(调用取得实例的方法如getInstance时才会实例化对象)(java中饿单例模式性能优于懒单例模式,c++中一般使用懒单例模式)
3.单例模式要素:
a.私有构造方法
b.私有静态引用指向自己实例
c.以自己实例为返回值的公有静态方法
1 饿汉式模式:
/ 懒汉式 在类进行装载的时候就进行初始化
// 优点:没有加任何的锁、执行效率比较高
// 在用户体验上来说,比懒汉式更好
// 缺点:类加载的时候就初始化,不管你用不用
// 浪费了内存,有可能占着茅坑不拉屎
// 绝对线程安全,在线程没出现以前就是实例化了。
public class HungrySingleton {
private HungrySingleton(){}
// 程序的执行顺序
//先静态、后动态
//先属性、后方法
//先上后下
private static final HungrySingleton hungrySingleton = new HungrySingleton();
// 用static关键字来进行声明getLazySingleton()方法,是因为LazySingleton类的构造方法被私有化了,没办法进行实例化,
// 所以需要通过你静态方法来进行获取实例
public static HungrySingleton getLazySingleton(){
return hungrySingleton;
}
}
2 懒汉式模式01
// 懒汉式 指全局的单例实例在第一次被使用时构建。
public class LazySingleton {
private LazySingleton(){}
private static LazySingleton lazySingleton = null;
// 用static关键字来进行声明getLazySingleton()方法,是因为LazySingleton类的构造方法被私有化了,没办法进行实例化,
// 所以需要通过你静态方法来进行获取实例
public static LazySingleton getLazySingleton(){
if(lazySingleton == null){
lazySingleton = new LazySingleton();
}
return lazySingleton;
}
// 不过懒汉式存在多线程的条件下会存在线程安全问题,因为在多线程的条件下可能会有两个线程在判断lazySingleton是否
// 为null时,都通过了验证,则会有两个实例。
// 而解决这个问题,就利用的同步锁来进行解决。
// 同步锁解决线程懒汉式安全问题的代码请看LaySingleton02.java
}
2 懒汉式模式02
// 利用同步锁解决懒汉式的线程安全问题
public class LazySingleton02 {
private LazySingleton02(){}
private static LazySingleton02 lazySingleton02 = null;
// 该线程会存在的问题是,在多线程的时候会阻塞在这里(需要拿到同步锁才能向下执行),造成效率下降
public static synchronized LazySingleton02 getLaySingleton02() {
if(lazySingleton02 == null){
lazySingleton02 = new LazySingleton02();
}
return lazySingleton02;
}
}
2 懒汉式模式03
// 懒汉式单例
// 特点:在外部类被调用的时候,内部类才会被加载
// 内部类一定是要在方法调用之前初始化
// 巧妙地避免了线程安全地问题
// 这种方式兼顾了饿汉式地内存浪费问题,也兼顾了synchronized性能问题
// 完美地屏蔽了这两个缺点
// 算是现在最好地实现单例模式地最好方案
public class LazySingleton03 {
private boolean initialized = false;
// 默认使用LazySingleton03地时候,会先初始化内部类
// 如果没有使用地话,内部类是不会被加载地
private LazySingleton03() {
synchronized (LazySingleton03.class) {
if (initialized == false) {
initialized = !initialized;
} else {
throw new RuntimeException("单例已被侵犯");
}
}
}
// 每一个关键字都不是多余地
// static 是为了使单例地空间共享
// 保证这个方法不会被重写,重载
public static final LazySingleton03 getInstance() {
return LazyHolder.LAZY;
}
private static class LazyHolder {
private static final LazySingleton03 LAZY = new LazySingleton03();
}
}
3 注册式单例模式01
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
// Spring中的做法,就是用这种注册时单例
public class BeanFactory {
private BeanFactory(){}
// 线程安全
private static Map ioc = new ConcurrentHashMap();
public static Object getBean(String className){
if(!ioc.containsKey(className)){
Object obj = null;
try{
obj = Class.forName(className).newInstance();
ioc.put(className,obj);
}catch (Exception e){
e.printStackTrace();
}
return obj;
}else{
return ioc.get(className);
}
}
}
3 注册式单例模式02
public class RegisterMap {
private RegisterMap(){}
private static Map register = new HashMap();
private static RegisterMap getInstance(String name){
if(name == null){
name = RegisterMap.class.getName();
}
if(register.get(name) == null){
register.put(name,new RegisterMap());
}
return (RegisterMap)register.get(name);
}
}
4 序列化单例模式
// 反序列化时导致单例破坏
public class Seriable implements Serializable {
// 反序列化就是说把内存中地状态通过转换成字节码地形式
// 从而转换成一个IO流,写入到其他地方(可以时磁盘、网络IO)
// 内存中永久保存下来了
// 反序列化
// 讲已经持久化的字节码内容,转换IO流
// 通过IO流的读取,进而讲读取的内容转换为java对象
// 在转换过程中重新创建对象new
private Seriable(){}
private final static Seriable INSTANCE = new Seriable();
public static Seriable getInstance(){
return INSTANCE;
}
private Object readResolve(){
return INSTANCE;
}
}