设计模式–单例模式(Singleton)
确保一个类只有一个实例,并提供该实例的全局访问点。
1、懒汉式–线程不安全
私有静态变量 instance 被延迟加载,只在第一次用到时进行实例化,若没有用到该类,就不会进行实例化,从而节约资源。
该方式在多线程环境下是线程不安全的,如果多个线程同时进入if (instance == null),并且此时
instance 为 null, 将会使 instance 实例化多次
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
2、饿汉式–线程安全
不管该类有没有用到,采取直接实例化的方式,这样也不会有线程安全问题。但丢失了延迟实例化带来的节约资源的好处
public class Singleton {
private static Singleton instance = new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
3、懒汉式–线程安全
只要对 getInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免多次实例化
但是当一个线程进入该方法后,其他试图进入该方法的线程不管 instance有没有被实例化,都必须等待。将使线程阻塞时间过长,因此存在性能问题。
public class Singleton {
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if (instance == null){
instance = new Singleton();
}
return instance;
}
}
4、双重校验锁–线程安全
instance只要被实例化一次,之后就可以直接使用了。因此加锁操作只需对实例化代码部分进行。双重校验先判断 instance是否已经实例化, 没有实例化在对实例化操作加锁。
public class Singleton {
private volatile static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if (instance == null){
synchronized(Singleton.class){
if(instance == null){
instance = new Singleton();
}
}
}
return instance;
}
}
为何要用两个 if 语句进行判断?
考虑当只有一个 if 的时候,在 instance == null的情况下,当多个线程都来执行时,虽然有加锁操作,但最终多个线程都会去执行加锁代码块中的内容,又将导致 instance实例化多次。因此必须使用双重 if 判断。
为何要使用 volatile 关键字修饰 instance?
对象实例化时分为三步执行:
1、为 instance 分配内存空间
2、初始化 instance
3、将 instance 指向分配的内存地址
由于JVM具有指令重排的特性,以上三步执行顺序可能发生改变。当线程1执行了 1、3步骤之后,线程2执行了方法 getInstance()发现 instance不为空,直接返回了 instance。但此时 instance 还未被初始化。使用 volatile可以禁止JVM的指令重排,保证多线程下也能正常执行。
本文介绍了单例模式的几种实现方法,包括懒汉式(线程不安全和线程安全)、饿汉式、双重校验锁(DCL)以及静态内部类和枚举实现。每种方式都有其优缺点,如懒汉式在多线程环境下的线程安全性问题,饿汉式的资源浪费,DCL的线程安全但可能的性能影响,以及静态内部类和枚举的简洁与线程安全。volatile关键字用于防止指令重排,确保多线程环境下的正确性。

674

被折叠的 条评论
为什么被折叠?



