如何创建Profile Data单例类?求正确实现方法
先给你揪出你提供的示例代码里的两个问题:
singleton是实例变量不是静态变量,静态方法getInstance根本访问不到,编译直接报错- 就算改成静态变量,这种写法线程不安全,多线程环境下大概率会创建多个实例
下面给你几种靠谱的ProfileData单例实现方式,按需选择:
一、饿汉式(最简单,线程安全)
类加载时就创建实例,适合初始化开销不大的场景。
class ProfileData { // 直接初始化全局唯一实例 private static final ProfileData INSTANCE = new ProfileData(); // 私有构造方法,禁止外部new实例 private ProfileData() {} // 全局访问入口 public static ProfileData getInstance() { return INSTANCE; } // 示例业务方法:获取用户昵称 public String getUserNickname() { return "Jugendra"; } }
- 优点:写法简单,天生线程安全,没有多线程同步问题
- 缺点:如果类初始化开销大且全程没用到,会浪费内存
二、线程安全的懒汉式(加同步锁)
真正用到时才初始化,保证线程安全,但性能稍差。
class ProfileData { private static ProfileData instance; private ProfileData() {} // 给方法加锁,同一时间只允许一个线程进入 public static synchronized ProfileData getInstance() { if (instance == null) { instance = new ProfileData(); } return instance; } // 示例业务方法:获取用户年龄 public int getUserAge() { return 28; } }
- 优点:真正懒加载,不用不占内存
- 缺点:每次调用
getInstance都要锁,高并发场景下效率低
三、双重检查锁定(推荐,兼顾懒加载和性能)
解决线程安全问题的同时,减少锁的开销。
class ProfileData { // volatile关键字禁止指令重排序,保证实例完全初始化后才被其他线程看到 private static volatile ProfileData instance; private ProfileData() {} public static ProfileData getInstance() { // 第一次检查:没初始化才进同步块 if (instance == null) { synchronized (ProfileData.class) { // 第二次检查:避免多个线程等锁后重复创建 if (instance == null) { instance = new ProfileData(); } } } return instance; } // 示例业务方法:获取用户邮箱 public String getUserEmail() { return "jugendra@example.com"; } }
- 优点:懒加载、线程安全,只有第一次初始化时加锁,后续调用直接返回实例,性能好
- 注意:必须给
instance加volatile,否则可能出现半初始化的实例被读取的问题
四、静态内部类实现(优雅的懒加载)
利用Java类加载机制保证线程安全,写法简洁。
class ProfileData { private ProfileData() {} // 静态内部类:只有调用getInstance时才会被加载 private static class ProfileDataHolder { private static final ProfileData INSTANCE = new ProfileData(); } public static ProfileData getInstance() { return ProfileDataHolder.INSTANCE; } // 示例业务方法:获取用户头像地址 public String getUserAvatarUrl() { return "/avatars/jugendra.png"; } }
- 优点:写法优雅,懒加载,线程安全,无性能开销。JVM加载外部类时不会加载内部类,只有调用
getInstance时才初始化实例,天然线程安全
五、枚举实现(最安全,复杂场景首选)
枚举是Java中唯一能绝对保证单例的方式,还能防止反射、序列化破坏单例。
enum ProfileData { INSTANCE; // 示例业务方法 public String getUserNickname() { return "Jugendra"; } public int getUserAge() { return 28; } }
使用方式:ProfileData.INSTANCE.getUserNickname();
- 优点:绝对线程安全,防反射、序列化破坏单例,写法最简单
- 缺点:类加载时就初始化,无法懒加载(多数场景可忽略)
内容的提问来源于stack exchange,提问作者Jugendra Bhati
相关产品推荐
相关产品推荐

