单例模式保证一个类仅有一个实例,并提供一个访问它的全局访问点。当系统需要某个类能有一个实例时,就可以采用单例模式。
- 单例类只能有一个实例
- 单例类必须自己创建自己的唯一实例
- 单例类必须给所有其他对象都能提供这一实例
保证单例模式仅有一个实例的核心思想就是构造方法私有化。
public class Singleton {//构造方法私有化private Singleton() {}//直接产生单例实例//直接定义了静态成员变量single,并通过new Singleton() 完成了初始化,之后不再变化,对象single是线程安全的private static final Singleton single = new Singleton();//提供单例对象方法//外部类可以通过静态方法 getInstance() 返回单例对象的实例public static Singleton getInstance() {return single;}
}
public class Singleton2 {private Singleton2() {}//先置为空private static Singleton2 single = null;//其他外部类调用getInstance() 方法时,再进行实例化并返回public static Singleton2 getInstance() {if (single == null) {single = new Singleton2();}return single;}
}
但是方式二存在线程安全问题,可能由两个外部类同时调用 getInstance() 方法,这样就会产生两个实例。有三种解决方法:
public static synchronized Singleton2 getInstance() {if (single == null) {single = new Singleton2();}return single;}
在方法前加了 synchronized 修饰,一个线程必须完全执行完 getInstance() 方法,下一个线程才能调用getInstance();
使用了双重锁,用两行相同的语句 if (single == null) ,第一句可以并行运行,第二句不可以。
public static Singleton2 getInstance() {if (single == null) {synchronized (Singleton2.class) {if (single == null) {single = new Singleton2();}}}return single;}
问:为什么synchronized括号里要传入Singleton2.class?
答:synchronized 关键字用于实现多线程环境下的同步,确保在同一时间只有一个线程可以访问被保护的代码块。在这个单例模式的实现中,synchronized 保证了在多线程环境下只会创建一个实例。 Singleton2.class 是一个类级别的对象,它是一个在 JVM 中唯一的对象。在 Java 中,每个类都有一个类对象,可以通过该类的 .class 属性获取。当使用 synchronized 时,如果传递一个类对象作为锁,则该锁对象将是全局唯一的,可以确保多线程环境下对该类实例的同步访问。因此,在这个实现中,Singleton2.class 被传递给 synchronized 作为锁对象,以确保在多线程环境下只会创建一个实例。
这是一种使用私有静态嵌套类实现单例设计模式的实现方式。 外部类 Singleton3 中有一个名为 My 的私有静态嵌套类,它拥有一个名为 single 的静态 final 字段,该字段持有 Singleton3 的实例。 通过将实例化的 Singleton3 对象作为静态 final 字段存储在内部类 My 中,保证了在整个应用程序中只有一个实例,并且该实例在运行时被延迟初始化。getInstance() 方法通过返回 My.single 来获取该实例。由于 single 字段是静态 final 的,因此在单例对象创建之后,任何尝试修改该对象的操作都将被忽略。
当Java虚拟机加载应用程序字节码时,单例对象并不是立即加载的,当第一次运行My.single时,单例对象才会动态生成。
public class Singleton3 {private static class My {private static final Singleton3 single = new Singleton3();}public static final Singleton3 getInstance() {return My.single;}
}
下一篇:【安全与风险】密码学介绍