Jira:线程安全的小工具数据?

时间:2014-06-12 21:31:30

标签: java multithreading jira jira-plugin

我有一些数据(两个HashSets和一个时间戳即时),我希望我的JIRA(OpenSocial?)小工具/插件可以分享所有请求 - 因为生成需要很长时间(几分钟)并且因为共享将有助于请求更高效。

有时(很少),请求可能包含一个参数,指示应刷新此共享数据。当然,这是第一次需要,它会被填充。数据代表一个陈旧的答案是可以的 - 它基于变化缓慢的事物并用于可视化趋势,因此可以容忍一个一个错误。

我想当JIRA启动时(或者我上传了我的附加组件的新版本)并且在前几分钟内有多个请求进入,我需要处理这些昂贵的共享数据的数量。一种线程安全的方式。目前结果看起来不错,但据我了解,这只是偶然的原因。

只有一个线程需要进行填充工作。在启动时,其他线程当然必须等待,因为它们不能空手而归。 (如果所有线程都进行了昂贵的初始化,那么服务器上会有很多不必要的负载)

但是在初始费用之后,如果有多个并发请求进入,其中一个请求包括“刷新'参数,只有一个线程需要付出代价 - 我很好地与其他线程使用昂贵数据的旧副本从而保持高效,并且在响应中包括"是某人在那里刷新了数据,但这里的结果是使用旧版本"。

有关数据的更多信息:两个HashSet和时间戳旨在表示一致的快照。 HashSet内容仅依赖于数据库中的值,而时间戳只是最近刷新的时间。这些数据都不依赖于任何早期的快照。而且它也不依赖于程序状态。时间戳仅用于回答问题"此数据的年龄和#34;粗略地说。每次刷新数据时,我都希望时间戳更新,但如果错误,任何事情都不会破坏。它只是用于调试和透明度。由于快照不依赖于早期快照或程序状态,因此可以将其包装并标记为易失。

最好的方法是否有明显的选择?替代品的利弊?

1 个答案:

答案 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;
    }
}