我有一个webservice调用来获取授权令牌并将其用于后续的webservice调用。现在我们之前做的是每当我们进行任何Web服务调用时,我们首先制作令牌Web服务,然后调用实际的Web服务。
获取令牌的方法如下所示。基本上,这段代码的作用是调用webservice获取令牌,并使用GSON解析响应并获取令牌。
public static String getAuthTicket() {
String authTicket = null;
HttpResponse httpResponse = getAuthResponse();
String body;
if (httpResponse.getStatusLine().getStatusCode() == 200) {
try {
body = IOUtils.toString(httpResponse.getEntity().getContent());
Gson gson = new GsonBuilder().disableHtmlEscaping().create();
ResponseTicket responseTicket = gson.fromJson(body, ResponseTicket.class);
authTicket = responseTicket.getTicket();
} catch (UnsupportedOperationException e) {
LOGGER.error("UnsupportedOperationException : ",e);
} catch (IOException e) {
LOGGER.error("IO Exception : ",e);
}
}
return authTicket;
}
这显然导致了性能问题。因此,提供Web服务以获取令牌的一方使令牌有效30分钟。
所以在上面的方法中我们想到的是将令牌与时间一起放入缓存并检查当前时间 - 缓存时间是否小于30.如果时间大于30,我们将进行服务调用以获取令牌并使用缓存中的时间戳更新令牌。
唯一让我担心的是同步,所以我不会因为竞争条件而得到腐败的认真。
我正在考虑将此静态方法设为同步。你认为还有其他更好的方法。
答案 0 :(得分:7)
答案是:取决于。
当多个线程在同一时间点访问共享数据时,会出现竞争条件。所以,当你有代码如:
private final Map<X, Y> sharedCache = new HashMap<>();
public static getAuthTicket() {
if (! sharedCache.containsKey...) {
sharedCache.put(...
...
您将受到竞争条件的影响 - 两个线程可以同时进入,并在同一时间更新该共享地图;导致各种各样的问题。
当我得到你的代码时 - 你会有类似的东西:
private static String cachedToken = null;
public static getAuthTicket() {
if (cachedToken == null || isTooOld(cachedToken)) {
cachedToken = getAuthTicketForReal();
}
return cachedToken;
}
您可能不希望两个线程并行调用getAuthTicketForReal()
。
所以,是的,使该方法同步是一种有效的方法。
其中:真正的问题是:添加该关键字足够吗?鉴于我的代码 - 答案是肯定的。您只是想避免这个缓存由多个线程“并行”设置。
最后:如果您担心在这里使用 synchronized 对性能产生影响 - 请忘记这一点。你在谈论一个多秒的“基于网络”的操作;所以你绝对不要担心 synchronized 可能产生的毫秒数(构成这个数字 - 关键是它:它太小了,在你操作的上下文中没关系正在做。)
关于您的评论:当然,使用 synchronized 意味着JVM将序列化调用该方法。这意味着当此方法需要1分钟返回时 - 对该方法的任何其他调用将阻止持续1分钟。
从这个意义上说;以一种在方法级别上不需要 synchronized 的方式来研究编写此方法的方法可能是一个很好的练习。例如,通过使用可以处理操作它们的多个线程的数据结构。