Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Java try-finally race condition?

A lot of Java resource usage examples look like this:

Resource r = openResource(); 
try { 
  // use resource
} finally { 
  r.close();
}

The declaration of r has to be outside of the try-clause to be visible in the finally-clause, but this also makes it look like there's a potential race condition: what if there's a thread interruption right between the openResource()-call and entering the try-clause?

Could that mean that the resource doesn't actually get closed in that scenario?

Or does Java guarantee that the try-finally covers r "fully", despite the syntax looking like it wouldn't?

Or do I have to write:

Resource r = null;  
try { 
  r = openResource();
  // use resource
} finally { 
  if (r != null) r.close();
}

in order to protect against thread interruptions?

like image 289
Jxtps Avatar asked Aug 11 '14 20:08

Jxtps


People also ask

How do I fix race conditions in Java?

In order to fix this race condition in Java, you need to wrap this code inside the synchronized block which makes them atomic together because no thread can go inside the synchronized block if one thread is already there.

What is Java race condition?

What is race condition? A condition in which the critical section (a part of the program where shared memory is accessed) is concurrently executed by two or more threads. It leads to incorrect behavior of a program.

How do you fix race conditions?

To avoid race conditions, any operation on a shared resource – that is, on a resource that can be shared between threads – must be executed atomically. One way to achieve atomicity is by using critical sections — mutually exclusive parts of the program.


2 Answers

what if there's a thread interruption right between the openResource()-call and entering the try-clause?

Then the thread won't throw an InterruptedException until it hits some blocking call. That can't happen before it gets into the try block, because there aren't any more blocking calls, assuming the method actually returns. From the docs for InterruptedException:

Thrown when a thread is waiting, sleeping, or otherwise occupied, and the thread is interrupted, either before or during the activity. Occasionally a method may wish to test whether the current thread has been interrupted, and if so, to immediately throw this exception.

Note that even if you do put the acquisition inside the try block, that doesn't really prevent any race condition that would otherwise exist - because you'd be relying on the method or constructor returning in the first place. If an exception can happen after the method/constructor returns, why can't it happen just before it returns, but after the resource has been acquired? If that happens, there's nothing you can call close on...

I'd still recommend using the try-with-resources statement in Java 7, but if you look at the JLS section 14.20.3.1 you'll see that the expansion is like your first piece of code:

The meaning of a basic try-with-resources statement:

try ({VariableModifier} R Identifier = Expression ...)
    Block

is given by the following translation to a local variable declaration and a try-catch-finally statement:

{
    final {VariableModifierNoFinal} R Identifier = Expression;
    Throwable #primaryExc = null;

    try ResourceSpecification_tail
        Block
    catch (Throwable #t) {
        ...
        #primaryExc = #t;
        throw #t;
    } finally {
       ...
    }
}
like image 135
Jon Skeet Avatar answered Sep 28 '22 05:09

Jon Skeet


Those Java resource usage examples could be written like the example you give where r is initially set to null, but if they used the Java 7 try-with-resources syntax (Assuming Resource implements AutoCloseable):

try (Resource r = openResource()) {
    // use resource
}

Then they would be equivalent to the first example, where openResource() is called prior to the try block. The Java Language Specification for the try-with-resources statement defines the semantics as equivalent to assigning the variable with the initializer before the block, and then entering the try-catch-finally block.

There appears to be a problem with an exception occurring before the try block is entered. The problem is largely theoretical, though. If openResource() returns normally and there are no intervening statements between the assignment to r and the beginning of the try block, then it is unlikely, though not impossible, that some other thread would get control before the try block began, but even then, when your thread started running again it would enter the try block without incident. If the other thread did something that caused the JVM to shut down, such as calling System.exit, then you typically would not need to worry about closing resources. And even this case is highly unlikely.

If, on the other hand, there was a problem opening the resource, presumably openResource() would throw, which would prevent r from ever being assigned.

like image 30
David Conrad Avatar answered Sep 28 '22 03:09

David Conrad