1

So I have been checking possibilities of using Closeable interface with ExecutorService. Then I saw Lukas answer here which is simply using method reference:

ExecutorService service = Executors.newSingleThreadExecutor();
try (Closeable close = service::shutdown) {

}

So i have been digging around and wanted to initialize service also in try block:

@Test(expected = RejectedExecutionException.class)
    public void singleThreadTest() throws Exception {

        ExecutorService es = null;

        try (Closeable closeable = (es = Executors.newSingleThreadExecutor())::shutdown){

            es.execute(()->System.out.println("executed " + Thread.currentThread().getId()));
        } 
        System.out.println("executed " + Thread.currentThread().getId());

        System.out.println(es.isShutdown());

        es.execute(()->{});

    }

This part quite ok eclipse a bit confused but if there is exception it will be supressed I believe, then I have tried:

@Test(expected = RejectedExecutionException.class)
    public void singleThreadTest() throws Exception {

        ExecutorService es = null;

        try (Closeable closeable = es::shutdown){

            es = Executors.newSingleThreadExecutor();
            es.execute(()->System.out.println("executed " + Thread.currentThread().getId()));
        } 
        System.out.println("executed " + Thread.currentThread().getId());

        System.out.println(es.isShutdown());

        es.execute(()->{});

    }

For this code block I get NullPointerException but the code block successfully executed (new thread created but at close() phase I got exception). I notice that while I was declaring :

Closeable closeable = es::shutdown

es has to be (effectively) final, this issue just occurs with double colon operator. i.e. below one doesnt compile:

try (Closeable closeable = new Closeable() {

            @Override
            public void close() throws IOException {
                es.shutdown();

            }
        }){

Is this a bug? What is the cause of this? *Method reference impl? *IDE *JVM (oracle - debian)?

Followup:

@Test(expected = RejectedExecutionException.class)
    public void singleThreadTest() throws Exception {

        ExecutorService es = null;


        es = Executors.newSingleThreadExecutor();
        es = Executors.newSingleThreadExecutor();
        es = null;
        try (Closeable closeable = ()->es.shutdown();){

            es = Executors.newSingleThreadExecutor();
            es.execute(()->System.out.println("executed " + Thread.currentThread().getId()));
        } 
        System.out.println("executed " + Thread.currentThread().getId());

        System.out.println(es.isShutdown());

        es.execute(()->{});

    }

This code doesnt compile but this does:

@Test(expected = RejectedExecutionException.class)
    public void singleThreadTest() throws Exception {

        ExecutorService es = null;


        es = Executors.newSingleThreadExecutor();
        es = Executors.newSingleThreadExecutor();
        es = null;
        try (Closeable closeable = es::shutdown;){

            es = Executors.newSingleThreadExecutor();
            es.execute(()->System.out.println("executed " + Thread.currentThread().getId()));
        } 
        System.out.println("executed " + Thread.currentThread().getId());

        System.out.println(es.isShutdown());

        es.execute(()->{});

    }
Community
  • 1
  • 1
HRgiger
  • 2,750
  • 26
  • 37
  • I find your text a bit confused. Like missing punctuation separating different sentences for example? And your source code is also formatted weird in some parts. Not exactly easy to read input. And for the record: at least to my knowledge, it is called a **method reference** not of double-colon operator. – GhostCat Mar 13 '17 at 14:15
  • @GhostCat I have updated, sorry for my english – HRgiger Mar 13 '17 at 14:26
  • @VinceEmigh can you check my update, you mean method reference can be compiled with no problem? – HRgiger Mar 13 '17 at 14:27
  • 1
    So which of these two questions is this a duplicate of? http://stackoverflow.com/q/33052917/476716 http://stackoverflow.com/q/4732544/476716 – OrangeDog Mar 13 '17 at 14:31
  • @OrangeDog first one:) Thx! – HRgiger Mar 13 '17 at 14:33
  • @VinceEmigh "es::shutdown is equivalent to () -> es.shutdown()" - the implementation is completely different – OrangeDog Mar 13 '17 at 14:33
  • @VinceEmigh other than method references are more efficient and don't require (effectively) final. Your source is the semantics, not the implementation. – OrangeDog Mar 13 '17 at 14:41
  • @VinceEmigh the OP question is why do they behave differently. How is explaining the difference irrelevant? Claiming that they are the same is not an answer. I do know exactly what you meant, and you are wrong. – OrangeDog Mar 13 '17 at 14:53
  • 1
    See also [What is the equivalent lambda expression for `System.out::println`](http://stackoverflow.com/a/28025717/2711488) – Holger Mar 13 '17 at 15:05
  • @Holger thx , seems like es can be captured as null but Objects.requireNonNull will fail at runtime – HRgiger Mar 13 '17 at 15:10
  • 3
    `Objects.requireNonNull` is for showing the equivalence. The spec mandates that the captured reference is tested for `null` immediately when being captured, like with `es::shutDown`, rather than deferring it to the moments when `Closeable.close()` is called. In either case, it would fail at runtime. – Holger Mar 13 '17 at 15:15
  • @Holger oh... so if it is compile time check how come last code block from my question compiled – HRgiger Mar 13 '17 at 15:17
  • 2
    No, no, it’s not a compile-time check (though your IDE might warn you). All I said, is that it will fail when *entering* the `try` block and `Closeable closeable = es::shutdown` is executed instead of waiting for the moment when leaving the `try` block and trying to invoke `close()`, which would call `shutdown`. That’s different to `ExecutorService es = null; try(Closeable closeable = () -> es.shutdown())`, which will fail at exit. – Holger Mar 13 '17 at 15:22
  • @Holger ok thx seems like i need a bit more reading about this – HRgiger Mar 13 '17 at 15:28

1 Answers1

1

No it's not a bug. The value of the variable/parameter you are closing over needs to be known at the point of capture.

When you get an NPE it's because you declared the anonymous inner class (or lambda or method reference) when es = null. That was the value that was captured: future assignments are not seen.

Why can method reference use non-final variables?

To stop you making this mistake there's a restriction "X has to be (effectively) final", meaning it won't compile if you try to change the value of a variable X that you are trying to capture.

Why are only final variables accessible in anonymous class?

Basically, you need to rearrange the code to something like this:

final ExecutorService es = Executors.newSingleThreadExecutor();
try (Closeable closeable = es::shutdown) {
    // ...
Community
  • 1
  • 1
OrangeDog
  • 36,653
  • 12
  • 122
  • 207