什么是单例模式?
单例模式是一种常用的软件设计模式,在应用这个模式时,单例对象的类必须保证只有一个实例存在,整个系统只能使用一个对象实例。不会频繁地创建和销毁对象,浪费系统资源。使用场景:IO 、数据库连接、Redis 连接等。
单例模式有 3 个特点:
- 单例类只有一个实例对象;
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点。
单例模式的优点:
- 单例模式可以保证内存里只有一个实例,减少了内存的开销。
- 可以避免对资源的多重占用。
- 单例模式设置全局访问点,可以优化和共享资源的访问。
单例模式的缺点:
- 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
- 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
- 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
单例模式代码实现饿汉式:天生线程安全
public class Singleton {
private static Singleton instance = new Singleton();
public static Singleton getInstance() {
return instance;
}
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
饱汉式:懒加载,线程不安全
public class Singleton {
private static Singleton instance;
public static Singleton getInstance() {
if(instance == null)
instance = new Singleton();
return instance;
}
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
饱汉式变种1线程安全:加synchronized 关键字,缺点同步效率低
public class Singleton {
private static Singleton instance;
public synchronized static Singleton getInstance() {
if(instance == null)
instance = new Singleton();
return instance;
}
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
或者
public class Singleton {
private static Singleton instance;
public static Singleton getInstance() {
synchronized(Singleton.class){
if(instance == null)
instance = new Singleton();
return instance;
}
}
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
饱汉式变种2:双重检查锁(Double Check Lock,简称DCL)变种2的核心是DCL,看起来变种2似乎已经达到了理想的效果:懒加载+线程安全。可惜的是,正如注释中所说,DCL仍然是线程不安全的,由于指令重排序,你可能会得到“半个对象”,即”部分初始化“问题。
public class Singleton {
private static Singleton instance;
public static Singleton getInstance() {
if(instance == null){
synchronized(Singleton.class){
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
饱汉式变种3:变种3专门针对变种2,可谓DCL 2.0。针对变种3的“半个对象”问题,变种3在instance上增加了volatile关键字,可达到线程安全
public class Singleton {
private static volatile Singleton instance;
public static Singleton getInstance() {
if(instance == null){
synchronized(Singleton.class){
if(instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
public static void main(String[] args) {
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1 == singleton2);
}
}
文章知识点概括来自