Java同步,线程安全,包装对象和竞争条件

时间:2013-08-14 17:40:37

标签: java multithreading thread-safety race-condition synchronized

我正在使用一个提交链式任务的线程池系统。每个任务都将运行,检查其结果,然后根据结果执行另一个任务。这是在同一个线程中完成的,因此不会将作业重新提交回线程池。每个任务都会将其结果添加到包装Collection的对象中,并提供一些其他次要功能,然后将其传递给链中的下一个任务。一旦到达链的末尾,结果将在Future.get()中返回并超时,并且可以分析完整的结果。

每个任务都将调用外部服务,即SOAP请求。如果任务挂起,则可能正在等待并等待响应。在这种情况下,如果get()超时,我会捕获TimeoutException并取消(true)未来。这意味着在任务线程中抛出了InterruptedException(因为它可能处于等待状态)并且可以被捕获。异常包装在响应对象中并放在Collection包装器中。

现在,如果超时,我似乎无法从Future对象中获取该集合。我正在寻找原始任务(或任务的包装器),如果捕获了TimeoutException并取消了Future,则从原始任务中检索Collection包装器,该包装器应该包含结果,直到超时。

但是,我不知道这本身是否是线程安全的或竞争条件。如果主线程在取消Future对象后立即尝试访问包装的Collection,那么包装的异常会在那里吗?是否会尝试并发迭代和修改?有没有办法确保在主线程检索之前包装的异常进入该集合?

一些Q& D示例代码:

public class MyCallable implements Callable<MyResponseCollection> {

    private MyResponseCollection responses = new MyResponseCollection();  //Wraps a List
    private MyTask task; //This is set prior to submitting the task wrapper

    public MyResponseCollection call() throws Exception() {
        task.setCollection(responses);
        task.call(); //kicks off the task chain, not an override to Callable.call
        return responses; //If all goes well, this is passed into the Future
    }
}

public class MyTask {

    private MyResponseCollection responses;

    public void setCollection(MyResponseCollection responses){
        this.responses = responses;
    }

    public void call(){
        try{
            MyResponse m = this.doStuff();
            responses.add(m);
            this.executeNext(m); //Runs the next task based on the response, simplified here, responses object passed into the next task

        } catch (InterruptedException e){
            responses.add(new ExceptionResponse(e)); //Here's where we catch that potential interrupt
        }
     }
     public MyResponse doStuff() throws InterruptedException{
         //All work done here
     }
}

这是我对多线程的第一次重大尝试,所以我不太确定如何确保线程之间的操作顺序等,或者如果我在这里做一些愚蠢的事情并且有一个更好的解决方案。 MyResponseCollection上的synchronize()块是否也适用于List中的?是否要求所述List也是Collections.synchronizedList?

1 个答案:

答案 0 :(得分:1)

为了让一个线程在释放资源时向另一个线程指示,我会在资源中添加Semaphore isDone字段。

public class MyResponseCollection {
    public final Semaphore isDone = new Semaphore(0);
}

public class MyCallable implements Callable<MyResponseCollection> {
    ...
    public MyResponseCollection call() throws Exception() {
        task.setCollection(responses);
        task.call(); //kicks off the task chain, not an override to Callable.call
        responses.isDone.acquire();
        return responses; //If all goes well, this is passed into the Future
    }
}


public class MyTask {
    ...

    public void call(){
        try{

        } finally {
            responses.isDone.release();
        }
     }
 }

responses.isDone.acquire()将阻止,直到许可证可用,isDone初始化为零许可。 MyTask#call()在其finally区块中添加了一个许可证,该许可证会唤醒MyCallable