您可以将令牌放入AtomicReference中,并使用信号量来暂停线程:
public class TokenWrapper { private final AtomicReference<Token> tokenRef = new AtomicReference<>(null); private final Semaphore semaphore = new Semaphore(Integer.MAX_VALUE); public TokenWrapper() { Token newToken = // refresh token tokenRef.set(newToken); } public Token getToken() { Token token = null; while((token = tokenRef.get()) == null) { semaphore.acquire(); } return token; } public Token refreshToken(Token oldToken) { if(tokenRef.compareAndSet(oldToken, null)) { semaphore.drainPermits(); Token newToken = // refresh token tokenRef.set(newToken); semaphore.release(Integer.MAX_VALUE); return newToken; } else return getToken(); }}public class RESTService { private static final TokenWrapper tokenWrapper = new TokenWrapper(); public void run() { Token token = tokenWrapper.getToken(); Response response = // call service with token if(response.getStatus == 401) { tokenWrapper.refreshToken(token); } }}refreshToken()使用原子
compareAndSet上
tokenRef,以确保只有一个线程将刷新令牌,然后调用
drainPermits()上
semaphore导致其他线程等待,直到令牌被刷新。
getToken()如果不是
null,则返回令牌,否则等待
semaphore-循环完成,因为线程可能必须在
tokenRef设置为
null和
drainPermits()被调用之间旋转几个周期
semaphore。
编辑:修改了的签名,
refreshToken(Token oldToken)以便传递旧令牌而不是在方法内部读取-
这是为了防止RESTService_A刷新令牌,RESTService_B获取带有旧过期令牌的401,然后
refreshToken在之后调用RESTService_B的情况RESTService_A的调用
refreshToken已完成,导致令牌被刷新两次。使用新的签名,RESTService_B将传递旧的过期令牌,因此,
compareAndSet当旧令牌无法与新令牌匹配时,调用将失败,导致
refreshToken仅被调用一次。



