How to properly handle two threads updating the same row in a database

前端 未结 3 2058
夕颜
夕颜 2020-12-13 15:08

I have a thread called T1 for reading a flat file and parsing it. I need to create a new thread called T2 for parsing some part of this file and la

3条回答
  •  青春惊慌失措
    2020-12-13 15:40

    I'm not certain I understand the question, but it seems it would constitute a logic error for a thread T1 which is only processing, for example, records beginning with AA to mark the entire file as "Parsed"? What happens if, for example, your application crashes after T1 updates but while T2 is still processing BB records? Some BB records are likely to be lost, correct?

    Anyhow, the crux of the issue is you have a race condition with two threads updating the same object. The stale object exception just means one of your threads lost the race. A better solution avoids a race entirely.

    (I am assuming here that the individual record processing is idempotent, if that's not the case I think you have bigger problems as some failure modes will result in re-processing of records. If record processing has to happen once and only once, then you have a harder problem for which a message queue would probably be a better solution.)

    I would leverage the functionality of java.util.concurrent to dispatch records out to threaded workers, and have the thread interacting with hibernate block until all records have been processed, at which point that thread can mark the file as "Parsed".

    For example,

    // do something like this during initialization, or use a Guava LoadingCache...
    Map executors = new HashMap<>();
    // note I'm assuming RecordType looks like an enum
    executors.put(RecordType.AA_RECORD, Executors.newSingleThreadExecutor());
    

    then as you process the file, you dispatch each record as follows, building up a list of futures corresponding to the status of the queued tasks. Let's assume successfully processing a record returns a boolean "true":

    List> tasks = new ArrayList<>();
    for (Record record: file.getRecords()) {
        Executor executorForRecord = executors.get(record.getRecordType());
        tasks.add(executor.submit(new RecordProcessor(record)));
    }
    

    Now wait for all tasks to complete successfully - there are more elegant ways to do this, especially with Guava. Note you also need to deal with ExecutionException here if your task failed with an exception, I'm glossing over that here.

    boolean allSuccess = true;
    for (Future task: tasks) {
        allSuccess = allSuccess && task.get();
        if (!allSuccess) break;
    }
    
    // if all your tasks completed successfully, update the file record
    if (allSuccess) {
        file.setStatus("Parsed");
    }
    

提交回复
热议问题