2017-03-29 19 views
1

Spring Frameworkの@Async注釈をブロックしたり、結果を待たずに使用する方法はありますか?ここに私の質問を明確にするいくつかのコードは次のとおりです。Spring Asyncがブロックしていて、ネストされた非同期呼び出しを防止しています

@Service 
public class AsyncServiceA { 

    @Autowired 
    private AsyncServiceB asyncServiceB; 

    @Async 
    public CompletableFuture<String> a() { 
     ThreadUtil.silentSleep(1000); 
     return asyncServiceB.b(); 
    } 
} 


@Service 
public class AsyncServiceB { 

    @Async 
    public CompletableFuture<String> b() { 
     ThreadUtil.silentSleep(1000); 
     return CompletableFuture.completedFuture("Yeah, I come from another thread."); 
    } 
} 

と設定:私は、アプリケーションを実行すると

@SpringBootApplication 
@EnableAsync 
public class Application implements AsyncConfigurer { 

    private static final Log LOG = LogFactory.getLog(Application.class); 

    private static final int THREAD_POOL_SIZE = 1; 

    public static void main(String[] args) { 
     SpringApplication.run(Application.class, args); 
    } 

    @Bean 
    public CommandLineRunner commandLineRunner(ApplicationContext ctx) { 
     return args -> { 
      final AsyncServiceA bean = ctx.getBean(AsyncServiceA.class); 
      bean.a().whenComplete(LOG::info); 
     }; 
    } 

    @Override 
    @Bean(destroyMethod = "shutdown") 
    public ThreadPoolTaskExecutor getAsyncExecutor() { 
     final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); 
     executor.setCorePoolSize(THREAD_POOL_SIZE); 
     executor.setMaxPoolSize(THREAD_POOL_SIZE); 
     executor.initialize(); 
     return executor; 
    } 

    @Override 
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { 
     // omitted 
    } 
} 

エグゼキュータはAsyncServiceA.a()や葉を呼び出して行くが、それはまだ、プールからスレッドを保持していますCompletableFuture.get()メソッドで待機しています。プール内にスレッドが1つしかないので、AsyncServiceB.b()は実行できません。私が期待しているのは、スレッドがAsyncServiceA.a()を実行した後にプールに戻され、AsyncServiceB.b()を実行するために利用可能になることです。

これを行う方法はありますか?

注1:ListenableFutureでも試してみましたが、結果は同じです。

注2:私は、が正常にはそうのように、各メソッドにexecutorを与えることによって(@Asyncなし)手動でそれをやってきました:

AsyncServiceA

public CompletableFuture<String> manualA(Executor executor) { 
    return CompletableFuture.runAsync(() -> { 
     LOG.info("manualA() working..."); 
     ThreadUtil.silentSleep(1000); 
    }, executor) 
      .thenCompose(x -> asyncServiceB.manualB(executor)); 
} 

AsyncServiceB

public CompletableFuture<String> manualB(Executor executor) { 
    return CompletableFuture.runAsync(() -> { 
     LOG.info("manualB() working..."); 
     ThreadUtil.silentSleep(1000); 
    }, executor) 
      .thenCompose(x -> CompletableFuture 
        .supplyAsync(() -> "Yeah, I come from another thread.", executor)); 
} 

誰かが不思議に思っていた場合、ここにはThreadUtilがあります。

public class ThreadUtil { 

    public static void silentSleep(long millis) { 
     try { 
      Thread.sleep(millis); 
     } catch (InterruptedException e) { 
      throw new RuntimeException(e); 
     } 
    } 
} 

更新:非同期注釈を非ブロックを追加しました問題https://jira.spring.io/browse/SPR-15401

+0

あなたは最大プールサイズが1の場合に期待しています...非同期メソッドを処理するスレッドは1つしかないので、基本的に非同期メソッドはありません。そのスレッドはブロックされ、終了するまでプールに戻されません。 –

+0

私は上記の質問で私の期待を説明しました.CompletableFutureの操作はgetを呼び出すまでノンブロッキングです。私はどこでもget()を呼び出さないので、スレッドはこのCompletableFutureで完了し、プールに返されると期待します。残念ながら、@Asyncアノテーションはget()メソッドを呼び出してスレッドをブロックしています。私はそのスレッドから脱出したいと思います。私はこれがサイズ1のスレッドプールとは非同期ではないことを知っていますが、非ブロッキングシナリオでは魅力的に機能します。 – nyxz

+0

あなたの最後のコメントが分かりますか分かりません。私は、b()メソッド呼び出しに到達することはありません。つまり、a()はasyncが返すもので、** b()が別のスレッドで呼び出され、b()のプールにスレッドがありません。私はメソッドが入力されたときと戻ったときにログにアスペクトを追加したので、b()は決して期待できないと確信しています。私は数十回もデバッグしました。 – nyxz

答えて

2

@Asyncサポートが(そのことについてまたは7)Java8の存在の前に道だった春3.0以降春の一部となっています。それ以降のバージョンではCompletableFutureのサポートが追加されましたが、それでもメソッド呼び出しの単純な非同期実行に使用されます。 (initial implementationはコールを反映/表示します)。

コールバックを作成し、非ブロック操作を行うために、非同期サポートは設計されていないか、または意図されていませんでした。

ノンブロッキングサポートでは、リアクティブ/ノンブロッキングコアを備えたSpring 5を待つことをお勧めします。非同期サポートではノンブロッキングサポートのために常にsubmit a ticketにすることができます。

+0

ありがとう!参考までに、私は問題を追加しました - 更新された説明のリンク。 – nyxz

3

私はチケットhttps://jira.spring.io/browse/SPR-15401に応答しましたが、ここでも私はM. Deinumの回答を承認するように応答します。

@Asyncどのように動作するのか(AOPを介してメソッド呼び出しをデコレートする)は、メソッド全体を同期から非同期に切り替えることのみを行うことができます。つまり、メソッドは同期でなければならず、同期と非同期の組み合わせではありません。

ServiceAがスリープ状態になってから非同期ServiceBに代わって、一部の@Async ServiceCでスリーピングパートをラップし、ServiceBとCで作成する必要があります。そのようにして、ServiceAは非同期になり、@非同期アノテーション自体..

+0

私の無知を許してください。しかし、このアプローチでは、ServiceAが呼び出されたスレッドを、@Asyncサービス呼び出しのそれぞれで基礎となるget()呼び出しで呼び出すことはできませんか?もしそうなら、これはServiceAがWebサービスから開始されたときに悩まされることはありませんか? – Traker

関連する問題