1

我有一个无状态 Java 应用程序部署到一个 tomcat Web 服务器。由于数据的性质,在任何给定时间,所有 http 线程都必须处理不同的密钥(换句话说:所有线程必须处理不同的密钥)。

因此,我编写了一个模块,如果请求的键是当前正在进行的(在此之前的 http 帖子),则将 http 帖子排队。只有在上一个具有相同密钥的 http 帖子完成其处理后,我才会继续处理当前的 http 帖子。

我用 concurrenthashmap 写了一个简单的 while 循环来测试是否有任何先前的请求在进行中。性能低于标准,并且存在意外行为。这是代码片段:

//This part of code is place inside the servlet 
private static ConcurrentHashMap<String, String> transQueue = new ConcurrentHashMap<String, String>();
private void inQueuePoll(String queueKey) {
   while(transQueue.containsKey(queueKey)){     
      synchronized(this){
         try{
            Thread.sleep(50); // i know this is bad, any idea to improve this?
            logger.trace("Wait for Que Key: "+queueKey + " );

            }catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    transQueue.put(queueKey, "");
}

在每个 http 帖子的最后,在 finally 块中,我输入: transQueue.remove(queueKey) 以确保我已将其从并发哈希图中删除。然而,在我最糟糕的噩梦中,从服务器日志中,我注意到在我删除 queueKey 之前,另一个 http post 线程设法摆脱了上面的 while 循环并继续处理。

我在上面的代码中做错了什么来“排队”http线程吗?

也非常感谢任何关于我如何能更好地做到这一点的想法。

4

1 回答 1

0

首先,为了清楚起见,我能否重述您的情况。对于任何给定的键,所有请求都必须在单个线程中处理;但是,对其他密钥的请求可能会在处理对第一个密钥的请求的同时进行。

这让我觉得非常适合ExecutorService。我会将您的地图<String, ExecutorService>设为地图,其中键是您的队列键,值是单线程执行程序(可以通过Executors.newSingleThreadExecutor()获得)。

给定一个键,您将从地图中获取一个ExecutorService,并提交您的Callable以进行处理。因为Executor这个键是单线程的,所以你可以放心,它会等到该键的前一个请求处理完毕后再处理这个请求。然后,对get的调用将被阻塞,直到对该请求的处理完成。

于 2011-11-01T15:09:39.527 回答