我有一些数据(两个HashSets和一个时间戳即时),我希望我的JIRA(OpenSocial?)小工具/插件可以分享所有请求 - 因为生成需要很长时间(几分钟)并且因为共享将有助于请求更高效。
有时(很少),请求可能包含一个参数,指示应刷新此共享数据。当然,这是第一次需要,它会被填充。数据代表一个陈旧的答案是可以的 - 它基于变化缓慢的事物并用于可视化趋势,因此可以容忍一个一个错误。
我想当JIRA启动时(或者我上传了我的附加组件的新版本)并且在前几分钟内有多个请求进入,我需要处理这些昂贵的共享数据的数量。一种线程安全的方式。目前结果看起来不错,但据我了解,这只是偶然的原因。
只有一个线程需要进行填充工作。在启动时,其他线程当然必须等待,因为它们不能空手而归。 (如果所有线程都进行了昂贵的初始化,那么服务器上会有很多不必要的负载)
但是在初始费用之后,如果有多个并发请求进入,其中一个请求包括“刷新'参数,只有一个线程需要付出代价 - 我很好地与其他线程使用昂贵数据的旧副本从而保持高效,并且在响应中包括"是某人在那里刷新了数据,但这里的结果是使用旧版本"。
有关数据的更多信息:两个HashSet和时间戳旨在表示一致的快照。 HashSet内容仅依赖于数据库中的值,而时间戳只是最近刷新的时间。这些数据都不依赖于任何早期的快照。而且它也不依赖于程序状态。时间戳仅用于回答问题"此数据的年龄和#34;粗略地说。每次刷新数据时,我都希望时间戳更新,但如果错误,任何事情都不会破坏。它只是用于调试和透明度。由于快照不依赖于早期快照或程序状态,因此可以将其包装并标记为易失。
最好的方法是否有明显的选择?替代品的利弊?
答案 0 :(得分:1)
您希望使用锁定来同步对代码部分的访问,这些部分只需要一次执行一个线程。 SO和Oracle Java文档中有大量资源显示如何更详细地使用锁,但是这样的事情应该可以解决问题。
您的想法是,您希望维护最近生成的一组结果的副本,并始终返回该副本,直到您有一组新数据可用。
import java.util.concurrent.locks.ReentrantLock;
public class MyClass
{
private volatile MyObject completedResults;
private final ReentrantLock resultsLock;
private final ReentrantLock refreshLock;
public MyClass()
{
// This must be a singleton class (such as a servlet) for this to work, since every
// thread needs to be accessing the same lock.
resultsLock = new ReentrantLock();
refreshLock = new ReentrantLock();
}
public MyObject myMethodToRequestResults(boolean refresh)
{
MyObject resultsToReturn;
// Serialize access to get the most-recently completed set of results; if none exists,
// we need to generate it and all requesting threads need to wait.
resultsLock.lock();
try
{
if (completedResults == null)
{
completedResults = generateResults();
refresh = false; // we just generated it, so no point in redoing it below
}
resultsToReturn = completedResults;
}
finally
{
resultsLock.unlock();
}
if (refresh)
{
// If someone else is regenerating, we just return the old data and tell the caller that.
if (!refreshLock.tryLock())
{
// create a copy of the results to return, since we're about to modify it on the next line
// and we don't want to change the (shared) original!
resultsToReturn = new MyObject(resultsToReturn);
resultsToReturn.setSomeoneElseIsRegeneratingTheStuffRightNow(true);
}
else
{
try
{
completedResults = generateResults();
resultsToReturn = completedResults;
}
finally
{
refreshLock.unlock();
}
}
}
return resultsToReturn;
}
}