我有一个类扩展了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中,则此代码可以正常工作。如果我们同时给出不同请求的数量代码工作正常。
答案 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;
}
}
}
如果这不能解决你的问题,我希望至少它可以提供一些想法。