Java 实现 Token 安全缓存:使用 ReentrantLock 和单例模式实现并发安全的 Token 管理器

在 Java 服务端开发中,Access Token 管理器必须是单例(Singleton) ,以确保整个应用只维护一个 Token 副本,并使用强大的可重入锁(ReentrantLock) 来保证在高并发下的数据一致性。

1. Token 管理器结构:单例模式

我们使用最常用的懒汉式或饿汉式单例模式来创建 AccessTokenManager 类。这里以饿汉式为例,确保类加载时即完成初始化。

java 复制代码
public class AccessTokenManager {
    // 状态变量
    private String accessToken = null;
    private long expiresAt = 0L; // 精确到毫秒的过期时间戳

    // 线程安全的核心:可重入锁
    private final ReentrantLock lock = new ReentrantLock();

    // 单例实例(饿汉式)
    private static final AccessTokenManager INSTANCE = new AccessTokenManager();

    // 构造方法私有化
    private AccessTokenManager() {
        // 初始化配置
    }

    // 对外提供获取实例的静态方法
    public static AccessTokenManager getInstance() {
        return INSTANCE;
    }
    
    // ... getAccessToken 方法实现
}

2. 核心方法:getAccessToken 的实现逻辑

getAccessToken 方法负责检查当前 Token 的状态。其核心在于使用锁来保护 Token 刷新这一关键操作,同时允许大量并发的读操作在 Token 有效时无需等待锁。

逻辑流程:
  1. 快速检查: 检查当前 Token 是否在安全阈值(例如 10 分钟)内有效。如果有效,直接返回,避免锁竞争。

  2. 获取锁: 如果 Token 即将过期或已过期,线程尝试获取 ReentrantLock。此时,其他线程将被阻塞。

  3. 二次校验(Double Check): 在获取锁后,必须再次检查 Token 状态。因为可能在线程 A 释放锁的瞬间,线程 B 立即获得了锁,但线程 A 已经完成了刷新。

  4. 执行刷新: 如果二次校验仍显示 Token 无效,则线程 B 调用企业微信 API 执行刷新操作。

  5. 更新和释放: 更新 accessToken 和 expiresAt,并在 finally 块中释放锁。

Java 代码关键部分:
java 复制代码
private void refreshAccessToken() {
    // 实际调用企业微信 API 的代码
    // 假设 API 返回 { "access_token": "new_token", "expires_in": 7200 }
    
    // 1. 调用 API 获取新 Token 和有效期
    // APIResponse response = callWeComApi(); 
    
    // 2. 更新状态
    this.accessToken = "新获取的 Token"; // response.getAccessToken();
    // 设定过期时间为当前时间 + 7200秒 - 600秒安全边际
    this.expiresAt = System.currentTimeMillis() + (7200 - 600) * 1000L;
}

public String getAccessToken() {
    long currentTime = System.currentTimeMillis();
    
    // 1. 快速检查 (无需锁)
    if (accessToken != null && currentTime < expiresAt) {
        return accessToken;
    }

    // 2. 尝试获取锁进行刷新
    lock.lock(); 
    try {
        // 3. 二次校验 (在锁保护下)
        if (accessToken != null && currentTime < expiresAt) {
            return accessToken; // 已被其他线程刷新,直接返回
        }
        
        // 4. 执行刷新
        refreshAccessToken();
        return accessToken;

    } catch (Exception e) {
        // 记录日志或抛出异常
        throw new RuntimeException("Access Token 刷新失败", e);
    } finally {
        // 5. 确保锁释放
        lock.unlock(); 
    }
}

3. ReentrantLock 的优势

相比于使用 synchronized 关键字,ReentrantLock 在这里的优势在于:

  • 明确的锁定和释放: 必须手动调用 lock() 和 unlock(),强制要求开发者将释放锁的逻辑放置在 finally 块中,防止异常导致死锁。

  • 非阻塞尝试: ReentrantLock 支持 tryLock(),可以尝试获取锁,如果获取失败,线程可以选择做其他事情,而不是无限等待。

  • 条件变量: 支持 Condition,可以更精细地控制线程的等待和唤醒,虽然在这个简单的 Token 场景中用不到,但在更复杂的并发任务中非常有用。

通过这种设计,AccessTokenManager 成为了一个高可用、线程安全的 Token 抽象层,为上层的所有 API 调用提供了稳定的保障。


QiWe开放平台提供了后台直登功能,登录成功后获取相关参数,快速Apifox在线测试,所有登录功能都是基于QiWe平台API自定义开发。

相关推荐
智购科技无人售货机工厂店26 分钟前
玻璃瓶饮料破损率突然上升,排查发现是取货口缓冲垫老化了~YH
java·开发语言·人工智能·python·eclipse
枫叶丹435 分钟前
语音 AI 怎样边听边答:实时对话系统的工作原理
开发语言·人工智能·chatgpt·开源·php·agent·codex
xie0510_44 分钟前
Any类简要实现
开发语言·c++·算法
小狼Solar1 小时前
SAP MDG 功能范围说明(基于S/4HANA 2025)
java·开发语言
kimnoic1 小时前
Python常用标准库模块及查询使用方法
开发语言·python
知识分享小能手1 小时前
C学习教程,从入门到精通,C语言输入和输出(4)
c语言·开发语言·学习
道尔柯南1 小时前
C++ 入门基础详解:从第一个程序到核心语法特性
开发语言·c++
盛世宏博智慧档案1 小时前
Modbus TCP+PoE以太网温湿度传感器项目部署全流程,附网络调试实操
开发语言·tcp/ip·php·以太网·传感器·温湿度
ACP广源盛139246256731 小时前
Type-C 扩展坞方案选型笔记|国产 DP1.2 转 HDMI2.0 桥接芯片 GSV2201D@ACP 评估
开发语言·笔记·硬件架构·硬件工程·hdmi·国产芯片
yuniko-n1 小时前
【JUC】线程间通信
java·开发语言