开发者

Long running method causing race condition

开发者 https://www.devze.com 2023-01-01 08:29 出处:网络
I\'m relatively new with hibernate so please be gentle. I\'m having an issue with a long running method (~2 min long) and changing the value of a status field on an object stored in the DB. The pseudo

I'm relatively new with hibernate so please be gentle. I'm having an issue with a long running method (~2 min long) and changing the value of a status field on an object stored in the DB. The pseudo-code开发者_运维知识库 below should help explain my issue.

public foo(thing) {  
    if (thing.getStatus() == "ready") {
        thing.setStatus("finished");
        doSomethingAndTakeALongTime();
    } else {
        // Thing already has a status of finished. Send the user back a message.
    }
}

The pseudo-code shouldn't take much explanation. I want doSomethingAndTakeALongTime() to run, but only if it has a status of "ready". My issue arises whenever it takes 2 minutes for doSomethingAndTakeALongTime() to finish and the change to thing's status field doesn't get persisted to the database until it leaves foo(). So another user can put in a request during those 2 minutes and the if statement will evaluate to true.

I've already tried updating the field and flushing the session manually, but it didn't seem to work. I'm not sure what to do from here and would appreciate any help.

PS: My hibernate session is managed by spring.


Basically you need to let it run in a separate Thread to make the method to return immediately. Else it will indeed block until the long running task is finished. You can pass the entity itself to the thread, so that it can update the status itself. Here's a basic kickoff example using a simple Thread.

public class Task extends Thread {
    private Entity entity;
    public Task(Entity entity) {
        this.entity = entity;
    }
    public void run() {
        entity.setStatus(Status.RUNNING);
        // ...
        // Long running task here.
        // ...
        entity.setStatus(Status.FINISHED);
    }
}

and

public synchronized void foo(Entity entity) {
    if (entity.getStatus() == Status.READY) {
        new Task(entity).start();
    } else {
        // ...
    }
}

With the Status in an enum you can even use a switch statement instead of an if/else.

    switch (entity.getStatus()) {
        case READY: 
            new Task(entity).start();
            break;
        case RUNNING:
            // It is still running .. Have patience!
            break;
        case FINISHED:
            // It is finished!
            break;
    }            

For a more robust control of running threads, you may want to consider ExecutorService instead. Therewith you can control the maximum number of threads and specify a timeout.


What the method doSomethingAndTakeALongTime() is doing? is it for DB operation or just executing some business logic?

If its not doing any DB operation, and you got your status fine then you can persist the object before calling that method.

And if its doing some DB operation, then you need to wait for it. So, even if you put in thread you need to wait for that thread to complete (using thread.join() we can do that)

the thing is, before you persist you must have completed all operation based on you ORM object right? so try to optimized the logic for the method to get it executed before you persist.

thanks.

0

精彩评论

暂无评论...
验证码 换一张
取 消