同步一个特定的Web服务调用

时间:2011-01-24 16:08:52

标签: java multithreading webservice-client

我想知道解决这个问题的最佳方法是什么。我有一个(抽象地说)简单方法,它调用web服务并将结果存储在本地内存缓存中,如:

public Document callWebservice(SomeObject parameter) { 
    Document result = cache.get(parameter);
    if (result == null) {
        result = parse(retrieve(parameter));
        cache.put(result);
    }
    return result;
}

现在,如果Document在缓存中,它可以正常返回,没问题。在单线程环境中,这种方法也可以正常工作。但是,在多线程环境中,事实证明每个线程将转向'else'位,并多次调用webservice。

我可以在'else'部分中查找一个同步块,但是我认为它太“宽”了 - 即使它们调用完全不同的东西,整个方法也无法调用线程。

如果webservice被调用两次,只要请求不同(例如SomeObject参数,在这种情况下),就可以了。

现在,问题:在这种情况下采取的最佳方法是什么?

我想过将参数存储在(threadsafe)集合对象中。如果参数的内容相同,它将产生相同的hashCode / equals结果,并且将在集合对象中找到,表明另一个线程已在处理此请求。如果是这种情况,则可以暂停调用线程,直到webservice返回。 (我必须弄清楚如何使调用线程等待)。这是否可以锁定SomeObject参数对象? e.g:

private Map<SomeObject, SomeObject> currentlyProcessingItems = new ConcurrentHashMap<SomeObject, SomeObject>();
public Document callWebservice(SomeObject parameter) {
    if (currentlyProcessedItems.contains(parameter)) {
        parameter = currentlyProcessedItems.get(parameter);
    } else {
        currentlyProcessedItems.putIfAbsent(parameter);
    }
    synchronized(parameter) {
        Document result = cache.get(parameter);
        if (result == null) {
            Document result = parse(retrieve(parameter));
            cache.put(result);
        }
        currentlyProcessedItems.remove(parameter);
        return result;
    }
}

(注意:跟踪当前处理请求的逻辑,ConcurrentHashMap的使用和锁定可能不是最理想的或完全错误的)

不,我从未真正读过有关线程的书。我应该。

我很确定这个特殊问题很常见,我只是无法找到答案。如果我可以问的话,这样的情况叫什么(即锁定特定对象)?

4 个答案:

答案 0 :(得分:1)

警告,xml DOM对象是线程安全的。所以(忽略缓存本身的线程安全问题)你不能在多个线程之间共享Document实例。我们在自己的代码库中得到了一点点,所以我知道这是一个有问题的事实(不仅仅是一些理论问题)。

至于缓存本身,我相信番石榴有你想要的东西:a computing map

答案 1 :(得分:0)

Java 5及更高版本的类有助于完成这一任务。您可能会发现ConcurrentHashMap很有趣:http://www.javamex.com/tutorials/synchronization_concurrency_8_hashmap.shtml

答案 2 :(得分:0)

我自己正在考虑这个问题,特别是当retrieve(参数)需要很长时间时(对我而言,它连接到服务器以检查身份验证,后端处理/过滤请求等)。现在我还没有尝试过这个,但为了讨论,这听起来怎么样?

要使用缓存,必须从线程调用新的MyCache(key).getValue(),因为getValue将在方法getCacheValue()中被锁定,直到此值可用(对所有等待的线程)。

public class MyCache {

    private final static HashMap cacheMap = new HashMap();  // One Map for all
    private final static Vector fetchList = new Vector();   // One List for all

    private Object cacheValue;
    private boolean waitingState;

    public MyCache (Object key) {
        if (cacheMap.containsKey (key)) {       // somebody has done it
            cacheValue = cacheMap.get (key);
        } else {
            waitingState = true;
            if (fetchInProgress (key, false))   // someone else is doing it
                return;
            new Thread (new MyFetch (key)).start();
    }}

    synchronized private static boolean fetchInProgress (Object key, boolean remove) {
        if (remove) {
            fetchList.remove (key);
        } else {
            boolean fetchingNow = fetchList.contains (key);
            if (fetchingNow)
                return true;
            fetchList.addElement (key);
        }
        return false;
    }

    public Object getValue () {
        if (waitingState)
            getCacheValue (true);
        return cacheValue;
    }

    synchronized private void getCacheValue (boolean waitOnLock) {
        if (waitOnLock) {
            while (waitingState) {
                try {
                    wait();
                } catch (InterruptedException intex) {
        }}} else {
            waitingState = false;
            notifyAll();
    }}

    public class MyFetch implements Runnable {
        private Object fetchKey;
        public MyFetch (Object key) {
            fetchKey = key;     // run() needs to know what to fetch
        }

        public void run () {        // Grab the resource, handle exception, etc.
            Object fetchedValue = null; 
            // so it compiles, need to replace by callWebService (fetchKey);
            cacheMap.put (fetchKey, fetchedValue);  // Save for future use
            fetchInProgress (fetchKey, true);   // Remove from list
            getCacheValue (false);          // Notify waiting threads
}}}

答案 3 :(得分:0)

我已经给出了自己的想法,它实际上似乎工作正常 - 首先,它使用ConcurrentHashMap(虽然好的并发Set会更好,但尚未找到正确的实现)维护当前正在处理的对象列表。

在方法本身中,它会查找地图是否已包含标识符的实例。如果没有,则尝试使用putIfAbsent()插入标识符。如果在check和insert之间插入另一个线程,则putIfAbsent方法返回另一个线程的插入项,并使用该项。

然后,使用新标识符或现有标识符,启动同步块,锁定标识符实例。在synchronized块中,查询缓存,如果不返回结果,则调用webservice。实际上,这意味着执行相同请求的所有进程(由标识符标识)只能同时处理其中一个请求,因此当第一个请求调用Web服务时,其他请求排队等待。当轮到他们时,结果就在缓存中,等待的线程可以从那里检索它。

至于Document对象不是线程安全的,我不知道该怎么办呢。在我的本地机器上它似乎没有引起任何问题,但是像这样的东西有随意出现在生产中的习惯。格儿。我想这将不得不计算出来。