一、什么是单例模式
单例模式确保一个类只有一个实例,并提供一个全局访问点来访问这个唯一实例。对于一个软件系统中的一些类而言,只有一个实例很重要。例如一个系统只能有一个窗口管理器或文件系统,一个系统只能有一个计时工具等。
二、单例模式的结构
单例模式是最简单的设计模式,它只包含一个类,即单例类。对于Singleton(单例),在单例类的内部创建它的唯一实例,并通过公有静态方法getInstance()让客户端可以使用它的唯一实例;为了防止在外部对单例类实例化,将其构造函数的可见性设为private;在单例类内部定义一个Singleton类型的静态对象作为供外部共享访问的唯一实例。
三、单例模式实现
单例模式的实现代码如下:
public class Singleton{
private static Singleton instance=null; //静态私有成员变量
//静态构造函数
private Singleton{
}
//静态公有工厂方法,返回唯一实例
public static Singleton getInstance(){
if(Singleton==null){
instance=new Singleton();
return instance;
}
}
客户端测试代码:
public class Client{
public static void main(String[] args){
Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
//判断两个对象是否相同
if(s1==s2){
System.out.println("两个对象是相同实例.");
}
else{
System.out.println("两个对象是不同实例."):
}
}
}
编译代码并运行,输出结果为:
两个对象是相同实例.
四、饿汉式单例
饿汉式单例类在定义静态变量的时候实例化单例类,因此在类加载时单例对象就已创建。代码如下:
public static EagerSingleton{
private static final EagerSingleton instance=new EagerSingleton();
private EagerSingleton(){
}
public static EagerSingleton getInstance(){
return instance;
}
}
当类被加载时静态变量instance就会被初始化,此时类的私有构造函数会被调用,单例类的唯一实例就会被创建。
五、懒汉式单例
懒汉式单例在第一次调用getInstance()方法时实例化,在类加载时并不自行实例化,这种技术又称为延迟加载技术,即需要的时候再加载实例。为了避免多个线程同时调用getInstance()方法,可以使用关键字synchronized,代码如下:
public class LazySingleton{
private static LazySingleton instance=null;
private LazySingleton(){}
//使用synchronized关键字加锁,确保在任意时刻只有一个线程可执行该方法
synchronized public static LazySingleton getInstance(){
if(instance==null){
instance=new LazySingleton();
}
return instance;
}
}
在上述懒汉式单例类中,在getInstance()方法前面增加了关键字synchronized进行线程锁定,以处理多个线程同时访问的问题。上述代码虽然解决了线程安全的问题,但每次调用getInstance()时都需要进行线程锁定判断,在多线程高并发访问环境中将会导致系统性能大大降低。因此可以继续对懒汉式单例模式进行改进,通过分析不难发现无需对整个getInstance()方法进行锁定,只需对其中的代码instance=new LazySingleton()进行锁定即可。getInstance()可以进行如下改进:
...
public static LazySingleton getInstance(){
if(instance==null)
synchronized(LazySingleton.class){
instance=new LazySingleton();
}
}
return instance;
}
...
但是上述代码仍然存在单例对象不唯一的情况。为解决这个问题,可以使用“双重检查锁定”(Doubl-Check Locking)。使用“双重检查锁定”实现的懒汉式单例类的完整代码如下:
public class LazySingleton{
private volatile static LazySingleton instance=null;
private LazySingleton(){}
public static LazySingleton getInstance(){
//第一重判断
if(instance==null){
//锁定代码块
Synchronized(LazySingleton.class){
//第二重判断
if(instance==null){
instance=new LazySingleton(); //创建单例实例
}
}
}
return instance;
}
}
使用双重检查锁定来实现懒汉式单例类,需要在静态成员变量instance之前增加修饰符volatile。
六、饿汉式单例类和懒汉式单例类的比较
饿汉式单例类在类被加载时就将自己实例化,它的优点在于无需考虑多个线程同时访问的问题,可以确保单例的唯一性,但是在资源利用效率方面不如懒汉式;懒汉式实例类在第一次使用时创建,无须一直占用系统资源,实现了延迟加载,但是必须处理多个线程同时访问的问题。
七、单例模式使用环境
1.系统只需要一个实例对象,例如系统只需要一个唯一的序列号生产器或资源管理器
2.客户调用类的单个实例只允许使用一个公共访问点,除了该公共访问点,不能通过其他途径访问该实例。