ExecutorServiceを使用して複数のジョブを並列処理するスタンドアロンのJavaアプリ
ExecutorService es = Executors.newFixedThreadPool(10);
EJB Bean内で同じソリューションを再利用したいのですが、通常はJava EEコンテナーを残してすべてのスレッドリソースを制御するため、ThreadPoolを正しく初期化する方法はわかりません。同じコードを使用するだけですか、Jbossマネージドスレッドプールを取得する別の正しい方法がありますか?
EJBでこれを行う正しい方法は、Concurrency Utils API(Java EE7)の一部であるManagedExecutorServiceを使用することです。エンタープライズコードでJava.util.concurrentの一部であるExecutorServiceを使用しないでください。
ManagedExecutorServiceを使用することにより、新しいスレッドが作成され、コンテナーによって管理されます。
次の例は、私のサイト here からのものです。
ManagedExecutorServiceを使用して新しいスレッドを作成するには、最初にCallableを実装するタスクオブジェクトを作成します。 call()メソッド内で、別のスレッドで実行する作業を定義します。
public class ReportTask implements Callable<Report> {
Logger logger = Logger.getLogger(getClass().getSimpleName());
public Report call() {
try {
Thread.sleep(3000);
catch (InterruptedException e) {
logger.log(Level.SEVERE, "Thread interrupted", e);
}
return new Report();
}
}
次に、ManagedExecutorServiceのsubmit()メソッドに渡すことでタスクを呼び出す必要があります。
@Stateless
public class ReportBean {
@Resource
private ManagedExecutorService executorService;
public void runReports() {
ReportTask reportTask = new ReportTask();
Future<Report> future = executorService.submit(reportTask);
}
}
必須の警告:独自のスレッドをJava EEアプリサーバー(Tomcatを含む)で作成することは、パフォーマンス上の大きな問題になる可能性があり、ほとんどの場合、コンテナー機能を妨げるため、推奨されませんJNDIとして、動作中。新しいスレッドは、それらがどのアプリケーションに属しているかを知らず、スレッドコンテキストクラスローダーは設定されず、他の多くの隠れた問題があります。
幸いなことに、Java EEサーバーを取得して、Java EE 6 @Asynchronous
およびこの巧妙なデザインパターン。任意のJava EE 6認定サーバーに移植可能。
アプリケーションでこのEJBを作成します。
package org.superbiz;
import javax.ejb.Asynchronous;
import javax.ejb.EJB;
import javax.ejb.Stateless;
import Java.util.concurrent.Callable;
import Java.util.concurrent.Executor;
@Stateless(name="Executor")
public class ExecutorBean implements Executor {
@Asynchronous
@Override
public void execute(Runnable command) {
command.run();
}
}
次に、プレーンな依存性注入を介して、アプリケーション内の他の場所でこのBeanを参照できます(参照コンポーネントがサーブレット、リスナー、フィルター、他のEJB、JSF管理Beanの場合)。
@EJB
private Executor executor;
その後、通常どおりExecutor
を使用します。
コンポーネントが別のJava EEコンポーネントではない場合、次の方法でBeanを検索できます。
InitialContext initialContext = new InitialContext();
Executor executor = (Executor) initialContext.lookup("Java:module/Executor");
さて... Davidのソリューションは、次の理由でうまくいきませんでした。
私がやったことは次のとおりです。
私のインストール:
-JBOSS AS 7.1.1
-Java 1.6
-RHEL
- Gradle および Arquillian を使用して例を実行する:
@Stateless
public class ExecutorBean {
@Asynchronous
public void execute(Runnable command) {
command.run();
}
}
次に、クライアントは次のようになります。
@EJB ExecutorBean eb;
@Test
public void testExecutorBean() {
eb.execute(new YourCustomizedRunnableWhichDoesALotOfUsefulStuff());
assertFalse(!true);
}
ただし、注意してください。standalone.xml(または一般的に言えばJBOSSの設定ファイル)には「スレッドプール」というセクションがあります。(JBOSSASを使用する場合)それを見て、値をいじってください。 arquillianテストでスレッドを使用すると、キープアライブ時間が非常に長くなりますが、killされたスレッドを取得します。これは、arquillianのマイクロデプロイの方法と関係があると思います。テストが実行されている間...少なくとも私はそれを観察していると思いますが、一方で、すべての終了したスレッドは実際にはタスク/操作を完了したという意味でうまく動作しました。
この投稿がお役に立てば幸いです!
EE7より前は、JSR 237のWorkManagerを使用する必要がある場合があります
http://docs.Oracle.com/javaee/1.4/api/javax/resource/spi/work/WorkManager.html
この仕様は現在取り下げられていますが、一部のアプリサーバーはまだ実装しています。 WebSphere 8.5でIBMの実装を使用します- IBM WorkManager 。これは完全に管理されたリソースであり、管理コンソールで利用できます。 Oracleとのインターフェイス互換性がないことに注意してください。
IBMバージョンの例を次に示します。
@Resource(lookup = "wm/default")
WorkManager workManager;
public void process() {
try {
ArrayList<WorkItem> workItems = new ArrayList<WorkItem>();
for (int i = 0; i < 100; i++) {
// submit 100 jobs
workItems.add(workManager.startWork(new Work() {
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " Running");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public void release() {
System.out.println(Thread.currentThread().getName() + " Released");
}
}));
}
// wait for all jobs to be done.
workManager.join(workItems, WorkManager.JOIN_AND, 100000);
} catch (WorkException e) {
e.printStackTrace();
}
}
また、 Commonj Workmanager を認識しています。
JBossを使用している場合、org.jboss.seam.async.ThreadPoolDispatcherを使用できます。
ThreadPoolDispatcherは完全に管理されています。
他の便利なマネージクラスについては、パッケージorg.jboss.seam.asyncをご覧ください。