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

标签 java multithreading thread-safety race-condition synchronized

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

每个任务都将调用外部服务,即 SOAP 请求。如果任务挂起,它可能正在等待响应。在这种情况下,如果 get() 超时,我会捕获 TimeoutException 并取消(true) future 。这意味着任务线程中会抛出 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 上的同步() block 是否也适用于其中的列表?是否要求所述List也是Collections.synchronizedList?

最佳答案

为了让一个线程在释放资源时向另一个线程指示,我会向资源添加一个 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() 将阻塞,直到许 cocoa 用,isDone 初始化为零许可。 MyTask#call() 在其 finally block 中添加一个许可,该许可会唤醒 MyCallable

关于Java 同步、线程安全、包装对象和竞争条件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18238611/

相关文章:

java - 如何使用String.hashCode生成主键

java - 如何通过 JNI 的函数调用接收 double 组?

java - 使用 Jndi 的 oracle DB 的 Tomcat 连接池

concurrency - Quartz 调度程序实例线程安全吗?

ios - +(void) 初始化线程安全

java - Spring事务管理器不是线程安全的?

java - 当你在java中执行gettext时如何获取没有/n/t的字符串

python - 使用 Python Turtle 进行多线程处理

c# - 当一个线程调用 WaitOne 时,如何找到发回信号的其他线程?

c# - 在 WCF 中使用任务构建异步操作契约(Contract)的正确方法