欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

创建型--单例模式

程序员文章站 2022-07-13 23:44:34
...

概念


保证一个类仅有一个实例,并提供一个访问该实例的全局访问点。

该模式涉及到一个单一的类,该类自己负责创建自己的对象,同时确保只能有单个对象被创建。同时这个类提供了一种访问该唯一对象的方式,可以直接访问,不需要对类进行实例化。

注意:

  1. 单例类只能有一个实例。
  2. 单例类必须自己创建自己的唯一实例。
  3. 单例类必须为所有其他对象提供这一实例的访问入口。

优点:

  1. 在内存里只有一个实例,减少了内存的开销,尤其是频繁的创建和销毁实例。
  2. 避免对资源的多重占用(比如写文件操作)。
    缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不关心外面怎么样来实例化。

构造函数是私有的。
getInstance() 方法中需要使用同步锁 synchronized 防止多线程同时进入造成多次实例化。
 

单例模式的实现方式


懒:是指延迟初始化(Lazy初始化)。饿,则相反。
线程安全:是指支持多线程下的单例。线程不安全,则相反。

方式一:懒汉式。线程不安全。定义唯一实例 instance 时不赋初值(Lazy)。
package singleton;
public class Singleton {
	//getInstance方法是static,则在该方法中能使用的成员只能也是static的
	//不能不通过方法访问点直接在类外部访问成员,故设为private
	private static Singleton instance;
	//private构造器使对象不能在类外创建
	private Singleton() { }  
	//static:private构造器使得该方法不能在类外部用对象调用
	public static Singleton getInstance() { 
		if(instance == null)
			instance = new Singleton();
		return instance;
	}
}

优点:第一次调用才初始化,避免内存浪费。
缺点:不支持多线程下的单例。

方式二:懒汉式。线程安全。(Lazy)
//和方式一区别在于 getInstance 方法加了锁。
package singleton;
public class Singleton {
	private static Singleton instance;
	private Singleton() { }  
	public static synchronized Singleton getInstance() { 
		if(instance == null)
			instance = new Singleton();
		return instance;
	}
}

优点:第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证多线程下的单例,但加锁会影响效率。
使用环境:getInstance方法使用不太频繁时。

方式三:饿汉式。线程安全。定义唯一实例时会赋初值(非Lazy)

它基于 classloader 机制避免了多线程的同步问题,保证初始化 instance 时只有一个线程在工作。即使未必是因为调用getInstance导致类的初始化,也不能肯定一定是延迟初始化。

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

优点:没有加锁,执行效率会提高。
缺点:类加载时就初始化,浪费内存,易产生垃圾对象。

方式四:双检锁/双重校验锁。线程安全。(DCL,即 double-checked locking)(Lazy)
package singleton;
public class Singleton {
	private volatile static Singleton instance;  //这里 volatile 
	private Singleton() { }  
	public static Singleton getInstance() { 
		if(instance == null) {
			synchronized (Singleton.class){  //这里 synchronized ()
				if(instance == null) {
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
}

优点:安全,在多线程情况下能保持高性能。
使用环境:getInstance() 的性能对应用程序很关键时。

方式五:登记式/静态内部类。线程安全。(Lazy)

这种方式能达到双检锁方式一样的功效,但实现更简单。对静态域使用延迟初始化,应使用这种方式而不是双检锁方式。这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

这种方式同样利用了 classloader 机制来保证初始化 instance 时只有一个线程,它跟第 3 种方式不同的是:第 3 种方式只要 Singleton 类被装载了,那么 instance 就会被实例化(没有达到 lazy loading 效果),而这种方式是 Singleton 类被装载了,instance 不一定被初始化。因为 SingletonHolder 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。

package singleton;
public class Singleton {
	private static class SingletonHolder{  //私有静态内部类
		private static final Singleton INSTANCE = new Singleton();  // private static final
	}
	private Singleton() { }  
	public static final Singleton getInstance() {  // final 
		return SingletonHolder.INSTANCE;  //
	}
}
方式六:枚举。线程安全。(非Lazy)
package singleton;

public enum Singleton {  
    INSTANCE;  
}

优点:简洁,能避免多线程同步问题,自动支持序列化机制,可防止反序列化重新创建新的对象。