public class ThreadSafe implements ITaskCompletionListener {
private final Set<String> taskIds = new HashSet<String>();
private final Set<String> successfulIds = new HashSet<String>();
private final Set<String> cancelledIds = new HashSet<String>();
private final Set<String> errorIds = new HashSet<String>();
public ThreadSafe() {
}
// invoked concurrently
@Override
public void onCancel(String pTaskId) {
remove(pTaskId);
cancelledIds.add(pTaskId);
}
// invoked concurrently
@Override
public void onError(String pTaskId) {
remove(pTaskId);
errorIds.add(pTaskId);
}
// invoked concurrently
@Override
public void onSuccess(String pTaskId) {
remove(pTaskId);
successfulIds.add(pTaskId);
}
private void remove(String pTaskId) {
taskIds.remove(pTaskId);
}
}
答案 0 :(得分:8)
答案 1 :(得分:4)
您可以使用一个线程安全的集合,而不是在集合之间拥有大量集合和传递ID。
private final ConcurrentMap<String, State> idState = new ConcurrentHashMap<String, State>();
enum State { TASK, SUCCESS, CANCELLED, ERROR }
public void onSuccess(String taskId) {
idState.put(taskId, State.SUCCESS);
}
public void onCancelled(String taskId) {
idState.put(taskId, State.CANCELLED);
}
public void onError(String taskId) {
idState.put(taskId, State.ERROR);
}
public void remove(String taskId) {
idState.remove(taskId);
}
答案 2 :(得分:1)
方法onError
,onSuccess
和onCancel
可以由两个或多个线程并行调用,最终可以并行调用taskIds.remove
,这不是线程 - 安全
只需将这三种方法标记为synchronized
即可完成。
答案 3 :(得分:1)
这会使它变得不那么复杂,让它看起来不容易打破。
答案 4 :(得分:1)
这段代码充满了线程安全问题。
HashSet不是线程安全的(它基于非线程安全的HashMap - 竞争条件,最终导致infinite loops)。
其次,taskIds
集合中的ID与其他集合中的ID之间没有原子性,因此任务的存在是短暂的。
第三,代码隐含地假设任务状态只是inProgress - &gt;成功|错误|取消,并且没有并发任务执行。如果不是这样,则代码失败。
答案 5 :(得分:0)
我认为你应该在同时调用的方法中使用同步块。
示例
public void onCancel(String pTaskId) {
synchronized(this){
remove(pTaskId);
cancelledIds.add(pTaskId);
}
}
答案 6 :(得分:0)
感谢你们每个人的回复,也指出我粘贴的代码中明显的bloopers。我将介绍我正在解决的问题。
我将收到请求(每个请求都有一组任务ID);我有一个线程池,用于提交此请求(每个请求的批处理任务)。我将从客户端接受将在任务完成后调用的侦听器。在当前上下文中,我可以使用ExecutorCompletionService
等待与请求完成相对应的所有任务,但是,我不希望进行任何阻塞调用。
相反,我希望子类FutureTask
(覆盖done()
方法),这将在我的子类executor服务的newTaskFor()
中实例化。与一个请求相对应的所有未来任务将共享TaskCompletionListener
,并且覆盖done()
方法将调用success()
,error()
之一或取消。 TaskCompletionListener
将使用初始任务ID集(任务数)进行初始化,并且对于每次调用,将计数减1,之后将发出完成信号。
这是代码的修改版本,我希望这次我没有犯过很多错误(是的,再次感谢上次所有答案)。
public class TaskCompletionListener implements ITaskCompletionListener {
private final AtomicInteger taskCount;
private final IProcessCompletionListener completionListener;
public TaskCompletionListener(final HashSet<String> pTaskIds, IProcessCompletionListener pCompletionListener) {
taskCount = new AtomicInteger(pTaskIds.size());
completionListener = pCompletionListener;
}
// invoked concurrently
@Override
public void onCancel(String pTaskId) {
checkCompletion();
}
// invoked concurrently
@Override
public void onError(String pTaskId) {
checkCompletion();
}
// invoked concurrently
@Override
public void onSuccess(String pTaskId) {
checkCompletion();
}
private void checkCompletion() {
// method level confinement
int currentCount = taskCount.decrementAndGet();
if (currentCount == 0) {
completionListener.onProcessComplete();
}
}
interface IProcessCompletionListener {
void onProcessComplete();
}
}