单例模式(Singleton)
单例模式(SIngleton)的目的是为了保证一个进程中,某个类有且仅有一个实例。
因为这个类只有一个实例,因此,该类不能允许 new 的方式创建实例。
- 单例的构造方法必须是 private,这样就防止了调用方自己创建实例。
- 既然不能通过 new 创建实例,只能通过访问静态成员变量的方式了,而且成员变量不能定义成 public(调用者可以直接通过 Singleton.instance 的方式更改 ),所以需要提供一个 获取静态成员变量的 静态方法。
饿汉式
/**
* 饿汉式
* 类加载到内存后,就实例化一个单例,JVM 保证线程安全
* 简单实用,推荐使用!
* 唯一缺点:不管用到与否,类加载时就完成实例化
* (话说你不用的,你装在它干啥?)
*/
public class Singleton01 {
private static final Singleton01 INSTANCE = new Singleton01();
private Singleton01() {
}
public static Singleton01 getInstance(){
return INSTANCE;
}
}
懒汉式
饿汉式虽然很简洁、而且有 JVM 线程安全,但是 饿汉式,不管用没用到这个实例,都会进行初始化。
为了达到按需初始化的目的,人们对饿汉式进行了更改。
线程不安全的懒汉式
/**
* lazy loading
* 也称懒汉式
* 虽然达到了按需初始化的目的,但却带来了线程不安全的问题
*/
public class Singleton02 {
private static Singleton02 INSTANCE ;
private Singleton02(){}
public static Singleton02 getInstance(){
if(INSTANCE == null){ // ①
INSTANCE = new Singleton02(); // ②
}
return INSTANCE;
}
}
为啥线程不安全?
假设有两个线程同时调用 getInstance() 方法,当线程 1 走到上面代码 ① 处时,判断 INSTANCE 变量为空,走到了 if 方法里面,这时候 线程 2 刚好在 线程 1 执行 代码 ② 之前 走到了 代码 ① 处,并判断 INSTANCE 变量也为空,则会出现 INSTANCE 被赋值两次的情况。
线程安全的懒汉式
既然线程不安全,我们可以通过在静态方法上添加 synchronized 关键字,来进行线程同步
/**
* lazy loading
*
* 也称懒汉式
* 虽然达到了按需初始化的目的,但却带来了线程不安全的问题
* 可以通过 synchronized 解决问题,但也带来效率低下
*
*/
public class Singleton03 {
private static Singleton03 INSTANCE;
private Singleton03(){}
public static synchronized Singleton03 getInstance(){
if(INSTANCE == null){
INSTANCE = new Singleton03();
}
return INSTANCE;
}
}
虽然解决了线程安全的问题,但是效率比较低下。
因为每次调用 getInstance() 方法的时候,都会加锁
线程不安全的双重检查锁定
/**
* 双重检查锁定
*/
public class Singleton05 {
private static Singleton05 INSTANCE;
private Singleton05(){
}
public static Singleton05 getInstance(){
if(INSTANCE == null){
synchronized (Singleton05.class){
if(INSTANCE == null){
INSTANCE = new Singleton05();
}
}
}
return INSTANCE;
}
}
有效的解决了性能的问题,一旦 INSTANCE 被初始化,则后续的调用都不会加锁
但是这个虽然用到了同步,但是依然会有线程安全的问题。
即 cpu 对指令的重排序,会出现 INSTANCE 为 null 的情况。因为 CPU 会对 new Singleton05() 的 分配内存和赋值指令排序,会有 分配内存之后被 直接返回的情况出现,所以线程不安全。
线程安全的双重检查锁定
/**
* 双重检查锁定
*/
public class Singleton05 {
private static volatile Singleton05 INSTANCE;
private Singleton05(){
}
public static Singleton05 getInstance(){
if(INSTANCE == null){
synchronized (Singleton05.class){
if(INSTANCE == null){
INSTANCE = new Singleton05();
}
}
}
return INSTANCE;
}
}
只需要对 INSTANCE 变量加上 volatile 关键字,该改关键字会阻止 cpu 的指令重排序优化。
静态内部类
/**
* 静态内部类的方式
* JVM 保证线程安全
* 加载外部类时不会加载内部类,这样可以实现懒加载
*/
public class Singleton06 {
private Singleton06(){
}
private static class SingletonHandler{
private static final Singleton06 INSTANCE = new Singleton06();
}
public static Singleton06 getInstance(){
return SingletonHandler.INSTANCE;
}
}
比较完美的一种方式。
枚举的方式
public enum Singleton07 {
INSTANCE;
private String name = "world";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于