天天看点

设计模式系列----单例模式的详细解读

1.单例模式

简单点说,就是一个应用程序中,某个类的实例对象只有一个,你没有办法去new,因为构造器是被private修饰的,一般通过getInstance()的方法来获取它们的实例。

getInstance()的返回值是一个对象的引用,并不是一个新的实例,所以不要错误的理解成多个对象。单例模式实现起来也很容易,直接看demo吧

1.1 饿汉式(静态代码块)

public class Singleton {
private static Singleton singleton;
//构造器私有化,外部不能new
private Singleton() {
}
//静态代码块,只执行一次
static{
instance=new Singleton;
}
//返回静态代码块创建的实例
public static Singleton getInstance() {
 
 return instance;
}
}      

评价:能用,但是耗费内存

## 1.2 饿汉式(静态常量)
public class Singleton {  
   private static Singleton instance = new Singleton();  
   private Singleton (){}  
   public static Singleton getInstance() {  
   return instance;  
   }  
}      

评价:能用,但是耗费内存,第三推荐度

2.懒汉式(线程安全)

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

评价:当使用时才会创建,并且只会创建一次;线程安全,但是效率太低了,最好别用

3.静态内部类

```bash
public class Singleton {  
   private static class SingletonHolder {  
   private static final Singleton INSTANCE = new Singleton();  
   }  
   private Singleton (){}  
   public static final Singleton getInstance() {  
   return SingletonHolder.INSTANCE;  
   }  
}
``      

`

评价:这种方式使用了类装载的机制,保证了初始化实例时只有一个线程,在外部类被装载时,静态内部类并不会被装载,实现了懒加载。同时类的静态变量只会在第一次加载时初始化,所以在这里JVM帮我们保证了线程的安全性,在类初始化时,别的线程是无法进入的。

所以,第一推荐度,推荐使用

4.双重检查

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

评价:既实现了线程安全,有实现了懒加载(lazy loading),由于java内存模型一些原因偶尔失败,在实际开发中,也还可以,第四推荐度,推荐使用

5.枚举

public enum Singleton {  
   INSTANCE;  
   public void whateverMethod() {  
   }  
}
下面是测试一下枚举如何调用

public static void main(String args[]){
Singleton instance=Singleton.INSTANCE;
}      

评价:这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,很强,也简单至极(但是jdk1.5 才有的枚举,部分人有些生疏),第二推荐度,所以推荐使用

生活中的实际应用场景:

1 .外部资源:每台计算机有若干个打印机,但只能有一个PrinterSpooler,以避免两个打印作业同时输出到打印机。内部资源:大多数软件都有一个(或多个)属性文件存放系统配置,这样的系统应该有一个对象管理这些属性文件 
2. Windows的Task Manager(任务管理器)就是很典型的单例模式(这个很熟悉吧),想想看,是不是呢,你能打开两个windows task manager吗? 不信你自己试试看哦~ 
3. windows的Recycle Bin(回收站)也是典型的单例应用。在整个系统运行过程中,回收站一直维护着仅有的一个实例。 
4. 网站的计数器,一般也是采用单例模式实现,否则难以同步。 
5. 应用程序的日志应用,一般都何用单例模式实现,这一般是由于共享的日志文件一直处于打开状态,因为只能有一个实例去操作,否则内容不好追加。 
6. Web应用的配置对象的读取,一般也应用单例模式,这个是由于配置文件是共享的资源。 
7. 数据库连接池的设计一般也是采用单例模式,因为数据库连接是一种数据库资源。数据库软件系统中使用数据库连接池,主要是节省打开或者关闭数据库连接所引起的效率损耗,这种效率上的损耗还是非常昂贵的,因为何用单例模式来维护,就可以大大降低这种损耗。 
8. 多线程的线程池的设计一般也是采用单例模式,这是由于线程池要方便对池中的线程进行控制。 
9. 操作系统的文件系统,也是大的单例模式实现的具体例子,一个操作系统只能有一个文件系统。