0

I have the following method which is being called by multiple threads:

private final static Object lock = new Object();
public String createDirectory()
{
    File file = new File("D:"+File.separator+"test");
    if(!file.exists() || !file.isDirectory())//if file doesn't exist then create a new directory.
    {
        synchronized(lock)
        {
            if(!file.exists() || !file.isDirectory())//----> (1)
            {
                boolean isCreated = file.mkdir();
            }
        }
    }
    return file.getAbsolutePath();
}

Is it possible that the JVM optimizer will comment out the code marked as (1) in above given menthod? I am suspecting that because, the existence of directory is checked twice in immediate succession. Considering it as a unnecessary redundant checking JVM optimizer might comment out the line --> (1).

Mac
  • 1,711
  • 3
  • 12
  • 26
  • 2
    The JVM can never optimize away expressions involving method calls because it doesn't know if those method calls have side effects. – DaoWen Jun 17 '14 at 20:59
  • Thanks @DaoWen. Can you please provide an official link for that? – Mac Jun 17 '14 at 21:01
  • @Mac I doubt you could find an "official link" for that. Optimizations are implementation-dependent and are often proprietary. – Isaac Jun 17 '14 at 21:03
  • 1
    A JVM Only ever optimizes BYTECODE not java code. It optimizes by taking comon java patterns and mapping them to more efficient bytecode forms. It never "optimizes your java" http://www.cubrid.org/blog/dev-platform/understanding-jvm-internals/ – Nick Humrich Jun 17 '14 at 21:03
  • http://stackoverflow.com/questions/24233716/do-java-caches-results-of-the-methods may also help. A JVM never caches method return values. – M A Jun 17 '14 at 21:05
  • 2
    @DaoWen Not necessarily. If a function is known not to have any side effects, it can be optimized out. A trivial case is `boolean doNothing() {return true;}`. But in this case, your statement is true, because the `File` methods are going to eventually end up as OS calls, and the JVM can't assume those don't have side effects (and aren't affected by state other than their arguments). – yshavit Jun 17 '14 at 21:22
  • @yshavit: Thanks for putting some light onto it. So, it means that in case the method call on File were not en up as OS calls (Ex: if the definition of `exists()` and `isDirectory()` is something like `return someBooleanValue`), then there might a possibility of code optimization for above code? – Mac Jun 17 '14 at 21:31
  • 2
    @Mac Definitely. First, the methods can be inlined; the code in the methods is "copy-pasted" to the call site, thus saving on the overhead of calling the method. Also, if the method doesn't have any side effects and always returns the same value (or possibly returns a different value, but that value is never used -- I'm not sure how far the optimizations go, to be honest), the method can be removed altogether. You can see an example at https://gist.github.com/yshavit/73d4bb96dcdaecd652d8. If the JVM didn't optimize the methods out, that'd take a _long_ time. – yshavit Jun 17 '14 at 21:36
  • These answers are good yshavit. You should bring it out of the comments and make a separate answer. – Alex A. Jun 17 '14 at 21:51
  • @yshavit: So what you are saying that in case no OS calls were involved over here then the line 1 might have been commented out by the JVM because of Redundant calls .. ?? – Mac Jun 17 '14 at 21:52
  • 1
    @Mac It's not just the redundant calls, it'd be if each redundant call could be proven to do nothing. But basically the general rule of an optimizer is, "if you can tell it's there except by seeing that the method runs faster, then it's broken." – yshavit Jun 17 '14 at 21:59
  • @AlexA. Hm, I may later tonight... for now I need to get back to work. :( – yshavit Jun 17 '14 at 22:00
  • @yshavit: I have seen such omission of instruction within the code while writing swing application wherein I called `repaint()` method twice in immediate succession for a given component. After first `repaint()` is called , the second one isn't called. Although a lot of logics and painting instructions were confined in the `paint` method which is called up by the `repaint()` method internally. – Mac Jun 17 '14 at 22:04
  • @yshavit: Please put your comments as answer so that I can accept your answer as the most helpful one. – Mac Jun 19 '14 at 04:41
  • I'm not sure which parts of my comments you found most helpful. But you can answer and accept your own question. – yshavit Jun 21 '14 at 02:13

3 Answers3

1

No. It will not be optimized out.

It would be a bit rubbish if the JVM optimized out a standard double check lock pattern.

JonathanS
  • 211
  • 1
  • 4
1

No. Compiler optimizations don't alter a program's flow. Specifically, method invocations will never be skipped.

Isaac
  • 16,458
  • 5
  • 57
  • 81
1

As pointed out by @yshavit

Because the File methods are going to eventually end up as OS calls, and the JVM can't assume those don't have side effects (and aren't affected by state other than their arguments) so, the JVM will not optimize the code involving if(!file.exists() || !file.isDirectory()) by commenting out that section .

Mac
  • 1,711
  • 3
  • 12
  • 26
  • The JVM does not operate in terms of method invocations. It will inline the code of the called method and optimize the resulting code. Thus, when it detects that within the code the same variables are read without an in-between write nor thread synchronization, it might join multiple reads into a single read. Since `java.io.File` maintains a cache that lasts about a minute or so, two `exists` calls on the same `File` within a short time span are very unlikely to result in two OS calls. – Holger Sep 04 '14 at 12:38
  • @Holger So according to you there is a possibility that both the `exists` invocations on `File` would result in same value even though the `File` existence state is changed while in between the two calls? – Mac Oct 09 '14 at 08:34
  • I encountered that issue in the past and had to insert a wait to notice an external change. But keep in mind that this is only relevant to *external* changes where your `synchronized` statement has no relevance anyway. Regarding threads within your JVM your code is safe as the JVM might optimize doubled operations but will not remove the effects of proper synchronization. If there is a possibility that a thread leaving the `synchronized` statement has altered the state, a thread entering a `synchronized` block using the same lock instance must re-read the state. – Holger Oct 09 '14 at 09:10
  • Correct me if I am wrong..In nutshell I can be assured that the two calls to `exist` method on `File` will definitely show the latest state of `File` **because there is a synchronization involved in between the two calls**. – Mac Oct 09 '14 at 09:19
  • Just for clarity: synchronization is only relevant for changes *within the JVM* made by threads *synchronizing on the same instance*. For these, the second call (within the `synchronized` block) is guaranteed to read the most recent value. – Holger Oct 09 '14 at 09:28
  • @Holger : Ok got it. But I am still unclear about the correctness of my code(Correct exhibition of code across all platforms and all JVMs). Should I go ahead with this logic with a complete surety of its correctness? Please clarify. – Mac Oct 09 '14 at 09:40
  • Well, your code doesn’t handle correctly the case if the file exists but is not a directory. In that case you would have to delete the file before attempting to create the directory. Besides that, the code is unnecessarily complicated. `mkdir` will check for the existence anyway and do nothing if the directory already exists. Therefore, you could simply call `mkdir` without any previous `exists` calls. – Holger Oct 09 '14 at 10:15