如何为lazy val定义的AccessToken实现可刷新能力并保障线程安全
实现线程安全且支持自动刷新的Token方案
原有lazy委托仅支持单次初始化,无法满足定期刷新的需求,你可以通过双重校验锁+缓存过期时间的方案改造,对外仍然保持val的调用方式不变:
object MyObj { // 缓存Token和对应的过期时间戳(毫秒) private var tokenCache: Pair<String, Long>? = null // 锁对象,控制并发刷新 private val refreshLock = Any() // 提前5分钟刷新,规避临界期请求失败问题 private const val ADVANCE_REFRESH_MS = 5 * 60 * 1000L // 服务端下发的Token有效期1小时 private const val TOKEN_VALID_MS = 3600 * 1000L val accessToken: String get() { val currentTime = System.currentTimeMillis() // 第一层校验:无锁判断缓存是否有效,绝大多数请求都走这个分支 val currentCache = tokenCache if (currentCache != null && currentTime < currentCache.second - ADVANCE_REFRESH_MS) { return currentCache.first } // 缓存过期,加锁进入刷新逻辑 return synchronized(refreshLock) { // 第二层校验:加锁后再次判断,避免多线程排队时已经被其他线程刷新过 val latestCache = tokenCache if (latestCache != null && currentTime < latestCache.second - ADVANCE_REFRESH_MS) { latestCache.first } else { val newToken = getAccessTokenFromHttpApi(username, password) val newExpireTime = currentTime + TOKEN_VALID_MS tokenCache = newToken to newExpireTime newToken } } } }
关键特性说明
- 线程安全:同一时间最多只有一个线程执行Token刷新操作,双重校验机制保证未过期时的读操作完全无锁,性能损耗极低
- 平滑刷新:提前5分钟触发刷新,避免Token刚好到有效期临界点时请求被服务端拒绝
- 兼容原有逻辑:对外暴露的仍然是只读
val属性,原有调用accessToken的代码不需要做任何修改
可选优化点
如果getAccessTokenFromHttpApi接口会返回准确的过期时长(比如接口返回expires_in字段),可以直接用接口返回的数值替换代码中硬编码的TOKEN_VALID_MS,时效性更准确。
如果遇到网络波动导致刷新请求失败,可以增加容错逻辑:只要旧Token还未到真正的过期时间,就暂时返回旧Token,避免业务直接报错。
内容的提问来源于stack exchange,提问作者k314159
相关产品推荐
相关产品推荐

