春天mvc中的DeferredResult

时间:2015-01-05 06:41:36

标签: spring spring-mvc deferred

我有一个类扩展了DeferredResults并扩展了Runnable,如下所示

public class EventDeferredObject<T> extends DeferredResult<Boolean> implements Runnable {

    private Long customerId;

    private String email;

    @Override
    public void run() {

        RestTemplate restTemplate=new RestTemplate();

        EmailMessageDTO emailMessageDTO=new EmailMessageDTO("dineshshe@gmail.com", "Hi There");

        Boolean result=restTemplate.postForObject("http://localhost:9080/asycn/sendEmail", emailMessageDTO, Boolean.class);

        this.setResult(result);
    }

//Constructor and getter and setters
}

现在我有控制器返回上述类的对象,每当有新请求到达控制器时,我们检查HashMap中是否存在该请求(在该实例中存储未处理的请求)。如果不存在那么我们正在创建对象EventDeferredObject类可以将其存储在HashMap中并在其上调用start()方法。如果此类型请求已经存在,那么我们将从HashMap返回该请求。如果请求完成,我们将从HashMap中删除该请求。

@RequestMapping(value="/sendVerificationDetails")

public class SendVerificationDetailsController {


private ConcurrentMap<String , EventDeferredObject<Boolean>> requestMap=new  ConcurrentHashMap<String , EventDeferredObject<Boolean>>(); 

    @RequestMapping(value="/sendEmail",method=RequestMethod.POST)
    public EventDeferredObject<Boolean> sendEmail(@RequestBody EmailDTO emailDTO)
    {
        EventDeferredObject<Boolean> eventDeferredObject = null;

        System.out.println("Size:"+requestMap.size());

        if(!requestMap.containsKey(emailDTO.getEmail()))
        {
            eventDeferredObject=new EventDeferredObject<Boolean>(emailDTO.getCustomerId(), emailDTO.getEmail());
            requestMap.put(emailDTO.getEmail(), eventDeferredObject);

            Thread t1=new Thread(eventDeferredObject);
            t1.start();

        }
        else
        {
            eventDeferredObject=requestMap.get(emailDTO.getEmail());

        }
        eventDeferredObject.onCompletion(new Runnable() {

            @Override
            public void run() {
                if(requestMap.containsKey(emailDTO.getEmail()))
                {   
                    requestMap.remove(emailDTO.getEmail());
                }
            }
        });

        return eventDeferredObject;
    }

}

现在,如果没有相同的请求存储在HashMap中,则此代码可以正常工作。如果我们同时给出不同请求的数量代码工作正常。

1 个答案:

答案 0 :(得分:1)

好吧,我不知道我是否理解正确,但我认为您可能在代码中有竞争条件,例如:

        if(!requestMap.containsKey(emailDTO.getEmail()))
        {
            eventDeferredObject=new EventDeferredObject<Boolean>(emailDTO.getCustomerId(), emailDTO.getEmail());
            requestMap.put(emailDTO.getEmail(), eventDeferredObject);

            Thread t1=new Thread(eventDeferredObject);
            t1.start();

        }
        else
        {
            eventDeferredObject=requestMap.get(emailDTO.getEmail());

        }

考虑一种情况,其中您有两个具有相同密钥emailDTO.getEmail()的请求。 请求1检查地图中是否有密钥,找不到密钥并将其放入。 请求2在一段时间后出现,检查地图中是否有密钥,找到它,以及 去拿它;然而就在此之前,由请求1启动的线程完成,而另一个由onComplete事件启动的线程从地图中删除了该键。此时,

requestMap.get(emailDTO.getEmail())

将返回null,因此您将获得NullPointerException。 现在,这确实看起来很罕见,所以我不知道这是否是你看到的问题。

我会尝试修改代码如下(我自己没有运行它,所以我可能会有错误):

public class EventDeferredObject<T> extends DeferredResult<Boolean> implements Runnable {

    private Long customerId;

    private String email;

    private ConcurrentMap ourConcurrentMap;

    @Override
    public void run() {
        ...
        this.setResult(result);
        ourConcurrentMap.remove(this.email);
    }

//Constructor and getter and setters
}

因此DeferredResult实现有责任从并发映射中删除自己。此外,我不使用onComplete来设置回调线程,因为在我看来这是一个不必要的复杂问题。为了避免我之前谈到的竞争条件,人们需要以某种方式结合对条目的存在进行验证,并将其提取到一个原子操作中;这是通过ConcurrentMap的putIfAbsent方法完成的。因此我将控制器更改为

@RequestMapping(value="/sendVerificationDetails")
public class SendVerificationDetailsController {

    private ConcurrentMap<String , EventDeferredObject<Boolean>> requestMap=new  ConcurrentHashMap<String , EventDeferredObject<Boolean>>(); 

    @RequestMapping(value="/sendEmail",method=RequestMethod.POST)
    public EventDeferredObject<Boolean> sendEmail(@RequestBody EmailDTO emailDTO)
    {
        EventDeferredObject<Boolean> eventDeferredObject = new EventDeferredObject<Boolean>(emailDTO.getCustomerId(), emailDTO.getEmail(), requestMap);
        EventDeferredObject<Boolean> oldEventDeferredObject = requestMap.putIfAbsent(emailDTO.getEmail(), eventDeferredObject );

        if(oldEventDeferredObject == null)
        {
            //if no value was present before
            Thread t1=new Thread(eventDeferredObject);
            t1.start();
            return eventDeferredObject;
        }
        else
        {
            return oldEventDeferredObject; 
        }
    }
}

如果这不能解决你的问题,我希望至少它可以提供一些想法。