类只能生成一个实例,该类提供了一个全局访问点供外部获取该实例。Spring的IoC创建Bean对象时,默认的就是单例模式。
优点:防止其他对象对自己进行实例化,确保访问的都是一个实例,这样可以节约内存资源,提高系统性能,避免对共享资源的多重占用。
缺点:不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例就会引起数据的错误,不能保存彼此的状态。单例模式没有抽象层,不能扩展,且职责过重,违背了单一性原则
应用场景:网站计数器、线程池、数据库连接池、任务管理器等等。
饿汉式,静态常量(可以使用)
在类装载时完成了实例化,避免了线程同步问题,但是没有懒加载效果,可能会造成内存浪费
public class Singleton {
private Singleton() { //构造器私有化
}
//内部创建对象
private final static Singleton instance = new Singleton();
//提供静态方法返回对象
public static Singleton getInstance(){
return instance;
}
}
懒汉式:等第一次使用的时候再初始化,即“懒加载”。
线程不安全(在单线程环境中适用)
实际开发中不会使用,如果在多线程环境中,一个线程在执行 if 判断成功后还没有开始创建对象时,第二个线程也正好通过了 if 判断,这样就会产生两次实例化。
public class Singleton {
private static Singleton instance;
private Singleton(){ //构造器私有化
}
//在静态方法内创建对象,并返回,调用方法才会创建对象
public static Singleton getInstance(){
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
线程安全,同步方法(基本上从并行变成了串行,效率低,不推荐)(加在方法上,每次线程都会进行synchronized等待)
public class Singleton {
private static Singleton instance;
private Singleton(){ //构造器私有化
}
//在静态方法内创建对象,添加synchronized使线程安全
public static synchronized Singleton getInstance(){
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
双重检查(推荐使用的)(针对1和2的情况,如果多个线程通过了第一次 if 的判断,但因为synchronized存在,后续等待的线程不会再重复实例化对象。而且,除了初始化的时候会加锁,后续的调用都是直接返回,解决了多余的性能消耗。)
外层的 if 让后续的线程不再进行synchronized等待,而第二层 if 可以阻止多次实例化
其中volatile是Java提供的一种轻量级的同步机制。Java 语言包含两种内在的同步机制:同步块 和 volatile 变量,相比于synchronized(重量级锁),volatile更轻量级,因为它不会引起线程上下文的切换和调度。而在变量上添加volatile,作用相当于一个内存屏障,所以才有了双检锁。
public class Singleton {
//持有私有静态实例,防止被引用,此处赋值为null,目的是实现延迟加载(懒加载)
private static volatile Singleton instance = null;
//私有构造方法,防止被实例化
private Singleton() {
//判断对象是否创建,若创建则抛出异常。
if(instance!=null){
throw new RuntimeException();
}
}
//静态工程方法,创建实例并返回
public static Singleton getInstance() {
//synchronized关键字,加锁,可保证线程的安全性
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) { //双重检查,防止线程问题
instance = new Singleton();
}
}
}
return instance;
}
}
静态内部类(推荐使用)
public class Singleton {
private Singleton(){ }
//创建静态内部类,JVM只在第一次装载类时初始化,所以保证了线程的安全
private static class SingletonInterior{
private final static Singleton SINGLETON = new Singleton();
}
//在静态方法内创建对象,并返回,调用方法时装载SingletonInstance,创建对象
public static synchronized Singleton getInstance(){
return SingletonInterior.SINGLETON;
}
}
枚举(《Effective JAVA》中大为推崇)
可以避免多线程问题,还能防止反射或序列化重新创建新的对象,但相对的牺牲了静态工厂方法的优点,无法实现懒加载(本质上和饿汉模式相同,区别仅在于公有的静态成员变量)
public enum Singletion{
INSTANCE; //一个属性保证了是单例
....属性方法....
}