前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java中几种单例模式的实现

Java中几种单例模式的实现

作者头像
砒霜拌辣椒
发布2023-08-23 17:26:05
1630
发布2023-08-23 17:26:05
举报
文章被收录于专栏:HerculesHercules

1、饿汉式

代码语言:javascript
复制
public class Apple {
    private static Apple apple = new Apple();

    private Apple() {

    }

    public static Apple getInstance() {
        return apple;
    }
}

以上为饿汉式单例模式简单实现,此种方式不会产生线程不安全情况,且只会创建一个对象。

饿汉式单例有以下特点

  • 私有构造。
  • 静态私有属性和静态公有方法。

2、懒汉式

2.1、非线程安全
代码语言:javascript
复制
public class Banana {
    private static Banana banana;

    private Banana() {

    }

    public static Banana getInstance() {
        if (banana == null) {
            banana = new Banana();
        }
        return banana;
    }
}

如果有多个线程调用getInstance方法,都会进入if判断,从而造成重复实例化,不是真正的单例。

2.2、线程安全
代码语言:javascript
复制
public class Banana {
    private static Banana banana;

    private Banana() {

    }

    public static Banana getInstance() {
        synchronized (Banana.class) {
            if (banana == null) {
                banana = new Banana();
            }
        }
        return banana;
    }
}

上面的方式,虽然解决了线程安全问题,但是如果在初始化完成之后,每次调用获取还要再经过同步锁,多线程环境下会降低程序运行的效率。

2.3、双重检查
代码语言:javascript
复制
public class Banana {
    private static Banana banana;

    private Banana() {

    }

    public static Banana getInstance() {
        if (banana == null) {
            synchronized (Banana.class) {
                if (banana == null) {
                    banana = new Banana();
                }
            }
        }
        return banana;
    }
}

第一层检查是只有实例还未初始化才进入同步锁,如果已经初始化完成就直接返回,这样可以提升效率。 第二层检查是为了防止前一个线程实例化之后释放锁,后面多个线程串行再去实例化,所以要加个判断。

2.4、volatile禁止指令重排序
代码语言:javascript
复制
public class Banana {
    private static volatile Banana banana;

    private Banana() {

    }

    public static Banana getInstance() {
        if (banana == null) {
            synchronized (Banana.class) {
                if (banana == null) {
                    banana = new Banana();
                }
            }
        }
        return banana;
    }
}

因为apple = new Apple()创建对象有以下3个步骤:

  1. 在堆上开辟空间。
  2. 调用构造器,初始化实例。
  3. 返回地址给引用(让引用指向该对象)。

如果没有禁止指令重排序,可能发生的顺序为1->3->2,就是在内存上开辟空间后就直接返回地址给引用,这个时候还没真正的初始化完对象。等释放锁后,别的线程进入判断,这时候引用已经不是null了,直接拿去使用,其实这个对象在这个时候可能还是个半成品,那就有空指针异常了。

懒汉式单例有以下特点

  • 私有构造。
  • 静态私有属性和静态公有方法。
  • 线程安全。
  • 双重检查(为了兼顾效率和线程安全)。
  • volatile禁止指令重排序(防止返回半初始化对象)。

3、静态内部类

代码语言:javascript
复制
public class Cherry {
    private static class CherryHolder {
        private static Cherry instance = new Cherry();
    }

    private Cherry() {

    }

    /**
     * 当getInstance方法第一次被调用的时候,执行CherryHolder.instance,导致CherryHolder类得到初始化;
     * 而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Cherry的实例,
     * 由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。
     */
    public static Cherry getInstance() {
        return CherryHolder.instance;
    }
}

静态内部类单例有以下特点

  • 私有构造。
  • 静态内部类持有静态变量作为单例的实例。
  • 静态域由虚拟机初始化一次,保证线程安全。

4、枚举

代码语言:javascript
复制
public enum Durian {
    INSTANCE;

    public void doSomething() {
        System.out.println("doSomething");
    }
}

枚举单例有以下特点

  • 防止反射执行私有构造方法,会抛NoSuchMethodException异常。
  • 防止序列化,反序列化后还是同一个对象。
  • 防止克隆,会抛CloneNotSupportedException异常。

5、破坏单例的三种方式

  1. 反射
  2. 序列化
  3. 克隆

除了枚举,其它几种单例模式都会被以上3种方式破坏。解决方案如下:

防止反射 定义一个全局变量,当第二次创建的时候抛出异常。

代码语言:javascript
复制
private static boolean isCreate = false;
private Apple() {
    // 防止反射对单例的破坏
    if(isCreate) {
        throw new RuntimeException("已然被实例化一次,不能再实例化");
    }
    isCreate = true;
}

防止克隆破坏 重写clone(),直接返回单例对象。

代码语言:javascript
复制
@Override
protected Object clone() {
    return apple;
}

防止序列化破坏 添加readResolve(),返回单例对象。

代码语言:javascript
复制
private Object readResolve() {
    return apple;
}

参考链接

代码地址

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2021-05-27,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1、饿汉式
  • 2、懒汉式
    • 2.1、非线程安全
      • 2.2、线程安全
        • 2.3、双重检查
          • 2.4、volatile禁止指令重排序
          • 3、静态内部类
          • 4、枚举
          • 5、破坏单例的三种方式
          • 参考链接
          • 代码地址
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档