2

I have this method in Service class:

@Service
public class Service {
    @Async
    public synchronized void asyncTest() {
        try {
            System.out.println("entered");
            Thread.sleep(5000);
            System.out.println("exited");
            System.out.println(this);
        }
        catch (InterruptedException e ) {}
    }
}

When I call

@Test
public void asyncTest() throws InterruptedException {
    service.asyncTest();
    service.asyncTest();
    Thread.sleep(10000);
}

it produces:

entered
entered
exited
exited
...Service@26d74d5f 
...Service@26d74d5f 

although I would expect

entered
exited
...Service@26d74d5f 
entered
exited
...Service@26d74d5f 

due to the synchronized keyword. Why?

My xml configuration:

<task:annotation-driven mode="aspectj" executor="fisExecutor" scheduler="fisScheduler"/>
<task:executor id="fisExecutor" pool-size="10" />
<task:scheduler id="fisScheduler" pool-size="30" />
<task:executor keep-alive="60" id="cmsExecutor" pool-size="5-00" queue-capacity="0" rejection-policy="CALLER_RUNS"/>

Spring version: 4.1.9.RELEASE JDK 1.7

Test class annotations:

   @RunWith(SpringJUnit4ClassRunner.class)
   @ContextConfiguration(locations = "classpath*:/applicationContext-*.xml")
  • 1
    Most likely that's because you stop execution for 5s. Both method calls are asynchronous, that means working separately. So both of them print `"entered"` before 5s mark. – CrazySabbath Jan 15 '18 at 16:09
  • service refers to the same object each time you call asyncTest(), right? – twinklehawk Jan 15 '18 at 16:41
  • @twinklehawk yes – user2686559 Jan 15 '18 at 18:13
  • ...Service@26d74d5f is printed in both threads.. – user2686559 Jan 16 '18 at 09:38
  • I run it in a JUnit test but I observed the problem also in fully running app. – user2686559 Jan 16 '18 at 14:36
  • @EnableAsync is used in a xml form: – user2686559 Jan 16 '18 at 14:56
  • other task elements: – user2686559 Jan 16 '18 at 15:18
  • Some related questions: https://stackoverflow.com/questions/17373307, https://stackoverflow.com/questions/23536710/, https://stackoverflow.com/questions/6610563, https://stackoverflow.com/questions/10357249/. Though frankly this looks very myterious, possibly even need your detailed pom.xml and Executor implementation to solve. You should consider reporting at https://jira.spring.io. And still you should put your excat spring version in the question title, and that you are using AspectJ. Else readers will waste time with wring assumptions. – tkruse Jan 16 '18 at 15:34
  • And tell your exact Java JDK version. run java -version and provide the output. For your Test example, also provide the Test class annotations, and how the service instance is created. – tkruse Jan 16 '18 at 15:45
  • And consider wrapping the service method inside a `synchronized(this) {...}` block, and report the outcome. – tkruse Jan 16 '18 at 15:52
  • synchronized(this) {...} solved the problem - good workaround. Thank you. – user2686559 Jan 17 '18 at 14:21

1 Answers1

1

One approach is to change the method signature to ensure you get an async object from your API that you can control. For instance you can modify your code to include CompletableFutures, mimicking something like below:

  @Service
  public class Service {
      @Async
      public CompletableFuture<Void> asyncTest() {
          try {
              System.out.println("entered");
              Thread.sleep(5000);
              System.out.println("exited");
              System.out.println(this);
          }
          catch (InterruptedException e ) {}

          return new AsyncResult<Void>(null);
      }
  }

I used this site for snippets and investigation resources: http://www.baeldung.com/spring-async

  • This approach is not ideal for this example however in a typical API, the Void type could easily be replaced by an actual object to match the API. – Shane Burroughs Jan 16 '18 at 21:19
  • I am limited to jdk 1.7 (CompletableFuture missing there). Also, does this solve the synchronized problem? – user2686559 Jan 17 '18 at 14:18