带有while循环和Thread.sleep()的Java同步块
我想在一个类中同步两个哈希映射。带有while循环和Thread.sleep()的Java同步块,java,multithreading,locking,Java,Multithreading,Locking,我想在一个类中同步两个哈希映射。 我维护两个映射的原因是,我将一个任务分派到不同的服务器,并在一个HashMap中维护原始任务对象,同时在另一个HashMap中管理响应状态好的,不管发生了什么,我在task中调用了相同的sendTaskToAllEndpoints方法。TaskIsOnResponses,有效地在while循环中保持锁,直到新调用的任务也结束 我通过创建一个AtomicInteger对象来捕获这个bug,该对象在每次进入synchronized块时递增,当我离开它并将它们封闭在t
我维护两个映射的原因是,我将一个任务分派到不同的服务器,并在一个HashMap中维护原始任务对象,同时在另一个HashMap中管理响应状态好的,不管发生了什么,我在task中调用了相同的sendTaskToAllEndpoints方法。TaskIsOnResponses,有效地在while循环中保持锁,直到新调用的任务也结束 我通过创建一个AtomicInteger对象来捕获这个bug,该对象在每次进入synchronized块时递增,当我离开它并将它们封闭在try finally块中时递减。第一个任务完成后,我观察到AtomicInteger对象没有下降到0,而是在1和2之间上下波动 第一个任务似乎成功结束,因为没有任何东西阻止回调执行,但一旦第一个任务完成,并且调用了task.taskistone,该方法本身就创建了另一个task实例,并调用sendTaskToAllEndpoints,因此阻止了第二个任务的所有回调调用,依此类推,直到他们以暂停结束
谢谢大家的建议。您的同步代码看起来不错,不过您不需要在taskMap上进行同步,因为只有一个线程会更改它。此外,如果你把事情搞砸了,拥有依赖于获取多个锁的同步逻辑是一种容易导致死锁的方法;您的目标应该是在仍然保持线程安全的情况下,拥有更少而不是更多的锁定对象。您确定没有其他未发布的代码正在获取任何对象上的锁吗?isTaskOver是公共的,但不是线程安全的!你应该换成私人的。顺便说一句,使用循环+Thread.sleep避免活动等待。最好使用条件等待+notifyAll。我为使其工作所做的唯一更改是删除while循环中的同步块。这意味着阻止我的另一个线程运行的唯一东西是while循环中的同步块。我知道在C语言中,编译器可能为了优化程序,影响多线程程序的功能。Java中也有类似的东西吗?
HashMap<String, Map<InetAddress, TaskResponse>> taskResponseMap = new HashMap<>();
HashMap<String, Task> taskMap = new HashMap<>();
public void endpointHasRespondedCallback(TaskResponse taskResponse, InetAddress from) {
// Callback threads gets blocked here!
synchronized(taskResponseMap) {
synchronized (taskMap) {
Map<InetAddress, TaskResponse> taskResponses = taskResponseMap.get(taskResponse.taskUuid);
if (taskResponses == null || !taskResponses.containsKey(from)) {
// The response does not exists probably due to timeout
return;
}
taskResponses.put(from, taskResponse);
}
}
}
public void sendTaskToAllEndpoints(Task task) {
long taskStartedAt = System.currentTimeMillis();
HashMap<InetAddress, TaskResponse> taskResponses = new HashMap<>();
taskResponseMap.put(task.taskUuid, taskResponses);
taskMap.put(task.taskUuid, task);
for (InetAddress dest : getDestinationNodes()) {
sendTaskTo(dest, task);
messageResponses.put(dest, TaskResponse.emptyTaskResponse());
}
// Should wait for response to comeback till the timeout is over
while (System.currentTimeMillis() < taskStartedAt + timeoutInMillis) {
Thread.sleep(1000);
synchronized(taskResponseMap) {
synchronized (taskMap) {
if(isTaskOver(task.taskUuid)) {
Map<InetAddress, TaskResponse> responses = taskResponseMap.remove(task.taskUuid);
taskMap.remove(task.taskUuid);
task.taskIsDone(responses);
return;
}
}
}
}
// If the task is still sitting there, then it must have timed out!
synchronized(taskResponseMap) {
synchronized (taskMap) {
taskResponseMap.remove(task.taskUuid);
taskMap.remove(task.taskUuid);
}
}
}
// Do not synchronize purposefully since it is only being called in synchronized methods
public boolean isTaskOver(String taskUuid) {
Task task = taskMap.get(taskUuid);
if (task == null || !taskResponseMap.containsKey(task.taskUuid)) {
return true;
} else {
for (TaskResponse value : taskResponseMap.get(task.taskUuid).values()) {
if (value.status != TaskResponseStatus.SUCCESSFUL) {
return false;
}
}
}
return true;
}