问题的提出:
生活中常常会遇到这样的问题,一个国家只能有一个主席,一个学校只能有一个校长…也就是说某些事物具有唯一性,如果多于一个会发生意想不到的事情。这种情况在计算机中也同样存在,因为单例模式应运而生。
单例模式:
单例模式可以说是最简单也是最常见的设计模式,它属于创建型模式,它提供了一种创建对象的最佳方式。这个模式往往是说一个类有且只有一个由自己生成的对象,并且还有一个对外的可供访问该对象的一种方式。(通俗的讲,就是这个类的构造器被私有化了,并且在类里面只实例化了一个对象,如果你需要用到该类对象,你只能通过getInstance()
方法获得类自己生成的这个对象)
那么什么时候你需要用到单例模式呢?
比如说:
现在的操作系统大多是多线程的,在操作一个文件的时候,就不可避免地出现多个进程或线程同时操作一个文件的现象,所以所有文件的处理必须通过唯一的实例来进行。例如:日志类(如果不用单例模式,对文件的操作可能会无效,反正会发生很多不可言状的意外)
实现方式:
单例模式的实现方式一般有6种。
0. 懒汉式,线程不安全
这种方式是最基本的实现方式,这种实现最大的问题就是不支持多线程。因为没有加锁 synchronized
,所以严格意义上它并不算单例模式。
这种方式懒加载 很明显,不要求线程安全,在多线程不能正常工作。
public class Singleton {
private static Singleton INSTANCE;
private Singleton (){}
public static Singleton getInstance() {
if (INSTANCE== null) {
INSTANCE= new Singleton();
}
return INSTANCE;
}
}
1. 懒汉式,线程安全
这种方式具备很好的懒加载,能够在多线程中很好的工作
优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized
才能保证单例,但加锁会影响效率。
getInstance()
的性能对应用程序不是很关键(该方法使用不太频繁)。
public class Singleton {
private static Singleton INSTANCE;
private Singleton (){}
public static synchronized Singleton getInstance() {
if (INSTANCE== null) {
INSTANCE= new Singleton();
}
return INSTANCE;
}
}
2. 饿汉式
这种方式比较常用,但容易产生垃圾对象。
优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存。
它基于类加载机制避免了多线程的同步问题,不过,INSTANCE
在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用 getInstance()
方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化 INSTANCE
显然没有达到懒加载的效果。
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton (){}
public static Singleton getInstance() {
return instance;
}
}
3. 双检锁/双重校验锁 double-checked locking
这种方式采用双锁机制,安全且在多线程情况下能保持高性能。(推荐!)
getInstance()
的性能对应用程序很关键。
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;
}
}
4. 静态内部类
这种实现方式采用静态内部类的方式,作为单例,直接用classLoader
(*jvm类加载机制)*进行处理异步加锁问题,并减少内存消耗,同时实现了懒加载,线程安全
其他的实现方式为解决并发,主要通过使用synchronized来加互斥锁,进行同步控制。但某些情况下,jvm已经隐含的执行了同步,这种情况下就不用自己再来进行同步控制了。
public class Singleton
{
private Singleton(){}
private static class SingletonHandler {
private static Singleton singleton = new Singleton();
}
public static Singleton getInstance(){
return SingletonHandler.singleton;
}
}
(枚举是JDK1.5加入的新特性,所以用的人很少)
5. 枚举
这种实现方式还没有被广泛采用,但这是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。
这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。由于enum
是JDK1.5加入的新特性,对于很多人来说还不是太习惯这种写法,不过这确实是单例模式的最佳解决方案。
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}