Concurrency Utilities for Java EE を実装する API タスクの例
非同期 Bean および CommonJ Timer and Work Manager API を使用するアプリケーションをマイグレーションして、Concurrency Utilities for Java™ EE を使用することができます。
Concurrency Utilities for Java EE は、 アプリケーション・サーバー内で並行操作を実行するための標準化された手法を提供します。これは、非同期 Bean および CommonJ Timer and Work Manager のプログラミング・モデルの後継です。
- コード例で使用されるリソース
- 基本タスクの実装
- タスクの実行依頼
- タスク・グループの完了の待機
- グループ内の単一タスクの完了の待機
- タスクの失敗時の原因例外の取得
- 間隔の後で実行するワンタイム・タスクのスケジューリング
- 固定レートでの繰り返しタスクのスケジューリングと次の実行までの間隔の照会
- さまざまな間隔で実行する繰り返しタスクのスケジューリング
- タスクの実行の中断および再開
- 後続のタスク実行の停止
- コンテキスト・プロキシーの構成
- 実行スレッドのトランザクションで実行する複数インスタンス用のコンテキスト・プロキシーの構成
- 呼び出しスレッドの据え置き実行用のコンテキスト・タスクの作成
- 据え置き実行用のコンテキスト・タスクの追加オプション
- サブシステム・モニター
- イベント処理
- コンテキスト・プロキシーの呼び出し中に発生する障害のイベント処理
コード例で使用されるリソース
このページのコード例では、アプリケーションで以下のリソースが挿入または検索されたものと想定しています。
@Resource(lookup = "wm/default")
private com.ibm.websphere.asynchbeans.WorkManager abWorkManager;
@Resource(lookup = "wm/default")
private commonj.work.WorkManager cjWorkManager;
@Resource
private ContextService contextService;
@Resource(name = "java:app/env/jdbc/dsRef")
private DataSource dataSource;
@Resource
private ManagedScheduledExecutorService executor;
@Resource
private ManagedThreadFactory threadFactory;
@Resource(name = "java:comp/env/tm/default", lookup = "tm/default", shareable = false)
private TimerManager timerManager;
@Resource
private UserTransaction tran;
基本タスクの実装
このセクションでは、この資料の後半を通じて他の例で使用される、簡単なタスク実装の例をいくつか示します。非同期 Bean には、後で実行するためにスケジュールするタスクの場合は、別のインターフェース AlarmListener が必要です。CommonJ には、後で実行するためにスケジュールするタスクの場合は、別のインターフェース TimerListener が必要です。Concurrency Utilities for Java EE タスクは、Runnable または Callable に設定でき、タスクが即時実行用に実行依頼されるか、後で実行するようにスケジュールされるかに関係なく、どちらのインターフェースでも使用できます。場合によっては、何の変更も加えずに、非同期 Bean または CommonJ Work を Runnable として管理対象 executor に実行依頼することができます。Work の release メソッドは、実行中のスレッドの取り消しや中断を行う管理対象 executor の機能に置き換えられます。Work の isDaemon メソッドは、LONGRUNNING_HINT 実行プロパティーに置き換えられます。
以下は、次の素数を検索する非同期 Bean および CommonJ の Work タスクの例です。
public class PrimeFinderWork implements
com.ibm.websphere.asynchbeans.Work, commonj.work.Work {
private long num;
private volatile boolean released;
private long result;
public PrimeFinderWork(long startingValue) {
num = startingValue;
}
public boolean isDaemon() {
return false;
}
public void release() {
released = true;
}
public void run() {
while (!isPrime(num))
if (released || Thread.currentThread().isInterrupted())
throw new RuntimeException(new InterruptedException());
else
num++;
result = num++;
}
public long getPrimeNumber() {
if (result > 0)
return result;
else
throw new IllegalStateException();
}
}
以下は、次の素数を検索する非同期 Bean の AlarmListener タスクの例です。
public class PrimeFinderAlarmListener implements AlarmListener {
private volatile boolean aborted;
private int count;
private long num;
private long result;
public PrimeFinderAlarmListener(long startingValue) {
num = startingValue;
}
public void abort() {
aborted = true;
}
public void fired(Alarm alarm) {
while (!isPrime(num))
if (aborted || Thread.currentThread().isInterrupted())
throw new RuntimeException(new InterruptedException());
else
num++;
result = num++;
// optionally reschedule:
Object delays = alarm.getContext();
if (delays instanceof Integer)
alarm.reset((Integer) delays);
else if (delays instanceof int[] && count < ((int[]) delays).length)
alarm.reset(((int[]) delays)[count++]);
}
public long getPrimeNumber() {
if (result > 0)
return result;
else
throw new IllegalStateException();
}
以下は、次の素数を検索する非同期 Bean の TimerListener タスクの例です。
public class PrimeFinderTimerListener implements CancelTimerListener, TimerListener {
private volatile boolean aborted;
private int count;
private final long[] delays;
private long num;
private long result;
public PrimeFinderTimerListener(long startingValue, long... delays) {
num = startingValue;
this.delays = delays;
}
public void timerCancel(Timer timer) {
aborted = true;
}
public void timerExpired(Timer timer) {
while (!isPrime(num))
if (aborted || Thread.currentThread().isInterrupted())
throw new RuntimeException(new InterruptedException());
else
num++;
result = num++;
// optionally reschedule:
if (count < delays.length)
try {
TimerManager timerManager = (TimerManager) new InitialContext().lookup(
"java:comp/env/tm/default");
timerManager.schedule(this, delays[count++]);
} catch (NamingException x) {
throw new RuntimeException(x);
}
}
public long getPrimeNumber() {
if (result > 0)
return result;
else
throw new IllegalStateException();
}
}
以下は、次の素数を検索する Concurrency Utilities for Java EE の Runnable タスクの例です。
public class PrimeFinderRunnable implements Runnable {
private long num;
private long result;
public PrimeFinderRunnable(long startingValue) {
num = startingValue;
}
public void run() {
while (!isPrime(num))
if (Thread.currentThread().isInterrupted())
throw new RuntimeException(new InterruptedException());
else
num++;
result = num++;
}
public long getPrimeNumber() {
if (result > 0)
return result;
else
throw new IllegalStateException();
}
}
以下は、次の素数を検索する Concurrency Utilities for Java EE の Callable タスクの例です。
public class PrimeFinderTask implements Callable<Long> {
private long num;
public PrimeFinderTask(long startingValue) {
num = startingValue;
}
public Long call() throws InterruptedException {
while (!isPrime(num))
if (Thread.currentThread().isInterrupted())
throw new InterruptedException();
else
num++;
return num++;
}
}
以下は基本的なデータベース挿入を実行する非同期 Bean の Work タスクの例です。
public class DBInsertWorkAB implements Work, Serializable {
private static final long serialVersionUID = 2606824039439594442L;
private transient Thread executionThread;
private final String code;
private final String name;
private boolean released;
private volatile int result = -1;
public DBInsertWorkAB(String code, String name) {
this.code = code;
this.name = name;
}
public int getResult() {
return result;
}
public synchronized void release() {
released = true;
if (executionThread != null)
executionThread.interrupt();
}
public void run() {
synchronized (this) {
if (released)
throw new RuntimeException("Work was canceled");
executionThread = Thread.currentThread();
}
try {
DataSource ds = (DataSource) new InitialContext().lookup(
"java:app/env/jdbc/dsRef");
Connection con = ds.getConnection();
try {
PreparedStatement stmt = con.prepareStatement(
"INSERT INTO AIRPORTS VALUES(?,?)");
stmt.setString(1, code);
stmt.setString(2, name);
result = stmt.executeUpdate();
} finally {
con.close();
}
} catch (NamingException x) {
throw new RuntimeException(x);
} catch (SQLException x) {
throw new RuntimeException(x);
} finally {
synchronized (this) {
executionThread = null;
}
}
}
}
以下は基本的なデータベース挿入を実行する非同期 Bean の AlarmListener タスクの例です。
public class DBInsertAlarmListener implements AlarmListener {
private volatile int result = -1;
public int getResult() {
return result;
}
public void fired(Alarm alarm) {
String[] alarmContext = (String[]) alarm.getContext();
try {
DataSource ds = (DataSource) new InitialContext().lookup(
"java:app/env/jdbc/dsRef");
Connection con = ds.getConnection();
try {
PreparedStatement stmt = con.prepareStatement(
"INSERT INTO AIRPORTS VALUES(?,?)");
stmt.setString(1, alarmContext[0]);
stmt.setString(2, alarmContext[1]);
result = stmt.executeUpdate();
} finally {
con.close();
}
} catch (NamingException x) {
throw new RuntimeException(x);
} catch (SQLException x) {
throw new RuntimeException(x);
}
}
}
以下は基本的なデータベース挿入を実行する CommonJ の Work タスクの例です。
public class DBInsertWorkCJ implements Work, Serializable {
private static final long serialVersionUID = -8801347489043041978L;
private transient Thread executionThread;
private final String code;
private final String name;
private boolean isDaemon;
private boolean released;
private volatile int result = -1;
public DBInsertWorkCJ(String code, String name) {
this.code = code;
this.name = name;
}
public int getResult() {
return result;
}
public boolean isDaemon() {
return isDaemon;
}
public synchronized void release() {
released = true;
if (executionThread != null)
executionThread.interrupt();
}
public void run() {
synchronized (this) {
if (released)
throw new RuntimeException("Work was canceled");
executionThread = Thread.currentThread();
}
try {
DataSource ds = (DataSource) new InitialContext().lookup(
"java:app/env/jdbc/dsRef");
Connection con = ds.getConnection();
try {
PreparedStatement stmt = con.prepareStatement(
"INSERT INTO AIRPORTS VALUES(?,?)");
stmt.setString(1, code);
stmt.setString(2, name);
result = stmt.executeUpdate();
} finally {
con.close();
}
} catch (NamingException x) {
throw new RuntimeException(x);
} catch (SQLException x) {
throw new RuntimeException(x);
} finally {
synchronized (this) {
executionThread = null;
}
}
}
以下は基本的なデータベース挿入を実行する CommonJ の TimerListener タスクの例です。
public class DBInsertTimerListener implements TimerListener {
private volatile int result = -1;
private final String code;
private final String name;
public DBInsertTimerListener(String code, String name) {
this.code = code;
this.name = name;
}
public int getResult() {
return result;
}
public void timerExpired(Timer timer) {
try {
DataSource ds = (DataSource) new InitialContext().lookup(
"java:app/env/jdbc/dsRef");
Connection con = ds.getConnection();
try {
PreparedStatement stmt = con.prepareStatement(
"INSERT INTO AIRPORTS VALUES(?,?)");
stmt.setString(1, code);
stmt.setString(2, name);
result = stmt.executeUpdate();
} finally {
con.close();
}
} catch (NamingException x) {
throw new RuntimeException(x);
} catch (SQLException x) {
throw new RuntimeException(x);
}
}
}
以下は、基本的なデータベース挿入を実行する Concurrency Utilities for Java EE の Callable タスクの例です。
public class DBInsertTask implements Callable<Integer>, Serializable {
private static final long serialVersionUID = 5556464104788801400L;
private final String code;
private final String name;
public DBInsertTask(String code, String name) {
this.code = code;
this.name = name;
}
public Integer call() throws NamingException, SQLException {
DataSource ds = (DataSource) new InitialContext().lookup(
"java:app/env/jdbc/dsRef");
Connection con = ds.getConnection();
try {
PreparedStatement stmt = con.prepareStatement(
"INSERT INTO AIRPORTS VALUES(?,?)");
stmt.setString(1, code);
stmt.setString(2, name);
return stmt.executeUpdate();
} finally {
con.close();
}
}
}
タスクの実行依頼
3 つすべてのプログラミング・モデルで、プールされたスレッドで実行して結果を取得するための基本タスクを実行依頼する方法を提供しています。
非同期 Bean の例:
WorkItem workItem = abWorkManager.startWork(
new DBInsertWorkAB("DLH", "Duluth International Airport"));
ArrayList<WorkItem> items = new ArrayList<WorkItem>(1);
items.add(workItem);
if (abWorkManager.join(items, WorkManager.JOIN_AND, TIMEOUT_MS)) {
DBInsertWorkAB work = (DBInsertWorkAB) workItem.getResult();
int numUpdates = work.getResult();
}
CommonJ の例:
WorkItem workItem = cjWorkManager.schedule(
new DBInsertWorkCJ("HIB", "Chisholm-Hibbing Airport"));
if (cjWorkManager.waitForAll(Collections.singletonList(workItem), TIMEOUT_MS)) {
DBInsertWorkCJ work = (DBInsertWorkCJ) workItem.getResult();
int numUpdates = work.getResult();
}
Concurrency Utilities for Java EE example:
Future<Integer> future = executor.submit(
new DBInsertTask("INL", "Falls International Airport"));
int numUpdates = future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
タスクの実行依頼時のその他のオプション
タスクを実行依頼するときに、必要に応じてリスナーおよび開始タイムアウトを割り当てたり、長時間の実行が予想されるかどうかを示したりすることができます。開始タイムアウトは、非同期 Bean ではパラメーターとしてのみ使用できますが、CommonJ および Concurrency Utilities for Java EE の場合は、WorkListener または ManagedTaskListener の内部で開始タイムアウトを実装することができます。
非同期 Bean の例:
long startTimeout = TIMEOUT_MS;
boolean isLongRunning = true;
WorkItem workItem = abWorkManager.startWork(
new DBInsertWorkAB("SGS", "South Saint Paul Municipal Airport"),
startTimeout,
new WorkListenerAB(),
isLongRunning);
ArrayList<WorkItem> items = new ArrayList<WorkItem>(1);
items.add(workItem);
if (abWorkManager.join(items, WorkManager.JOIN_AND, Integer.MAX_VALUE)) {
DBInsertWorkAB work = (DBInsertWorkAB) workItem.getResult();
int numUpdates = work.getResult();
}
CommonJ の例:
long startTimeout = TIMEOUT_MS;
boolean isLongRunning = true;
DBInsertWorkCJ work = new DBInsertWorkCJ("STP", "Saint Paul Downtown Airport");
work.setDaemon(isLongRunning);
WorkItem workItem = cjWorkManager.schedule(
work, new WorkListenerCJ(work, startTimeout));
Collection<WorkItem> items = Collections.singleton(workItem);
if (cjWorkManager.waitForAll(items, WorkManager.INDEFINITE)) {
work = (DBInsertWorkCJ) workItem.getResult();
int numUpdates = work.getResult();
}
Concurrency Utilities for Java EE example:
long startTimeout = TIMEOUT_MS;
boolean isLongRunning = true;
Callable<Integer> contextualTask = ManagedExecutors.managedTask(
new DBInsertTask("LVN", "Airlake Airport"),
Collections.singletonMap(ManagedTask.LONGRUNNING_HINT,
Boolean.toString(isLongRunning)),
new TaskListener(startTimeout));
Future<Integer> future = executor.submit(contextualTask);
int numUpdates = future.get();
タスク・グループの完了の待機
3 つすべてのプログラミング・モデルで、タスク・グループの完了を待機する方法を提供しています。以下の例では、最大待機時間を指定します。これは、さまざまな Future に対する GET を順に呼び出すことで、無期限に待機したり、Concurrency Utilities for Java EE の細分度をさらに追加したりすることが可能なためです。
ArrayList<WorkItem> items = new ArrayList<WorkItem>(3);
items.add(abWorkManager.startWork(
new DBInsertWorkAB("COQ", "Cloquet/Carlton County Airport")));
items.add(abWorkManager.startWork(
new DBInsertWorkAB("CQM", "Cook Municipal Airport")));
items.add(abWorkManager.startWork(
new DBInsertWorkAB("CKN", "Crookston Municipal Airport")));
boolean allCompleted = abWorkManager.join(items, WorkManager.JOIN_AND, TIMEOUT_MS);
int numUpdates = 0;
for (WorkItem workItem : items) {
if (workItem.getStatus() == WorkEvent.WORK_COMPLETED) {
DBInsertWorkAB work = (DBInsertWorkAB) workItem.getResult();
numUpdates += work.getResult();
} else
((Work) workItem.getEventTrigger(Work.class)).release();
}
CommonJ の例:
List<DBInsertWorkCJ> workList = Arrays.asList(
new DBInsertWorkCJ("DTL", "Detroit Lakes Airport"),
new DBInsertWorkCJ("TOB", "Dodge Center Airport"),
new DBInsertWorkCJ("DYT", "Sky Harbor Airport"));
List<WorkItem> items = new ArrayList<WorkItem>(workList.size());
for (DBInsertWorkCJ work : workList)
items.add(cjWorkManager.schedule(work));
boolean allCompleted = cjWorkManager.waitForAll(items, TIMEOUT_MS);
int numUpdates = 0;
for (int i = 0; i < items.size(); i++) {
WorkItem workItem = items.get(i);
if (workItem.getStatus() == WorkEvent.WORK_COMPLETED) {
DBInsertWorkCJ work = (DBInsertWorkCJ) workItem.getResult();
numUpdates += work.getResult();
} else
workList.get(i).release();
}
Concurrency Utilities for Java EE example:
List<DBInsertTask> tasks = Arrays.asList(
new DBInsertTask("CFE", "Buffalo Municipal Airport"),
new DBInsertTask("CHU", "Caledonia-Houston County Airport"),
new DBInsertTask("CBG", "Cambridge Municipal Airport"));
int numUpdates = 0;
List<Future<Integer>> futures = executor.invokeAll(tasks, TIMEOUT_MS, TimeUnit.MILLISECONDS);
for (Future<Integer> future : futures)
numUpdates += future.get();
グループ内の単一タスクの完了の待機
3 つすべてのプログラミング・モデルで、グループ内の単一タスクの完了を待機する方法を提供しています。以下の例では、最大待機時間を指定します。ただし、無期限に待機することも可能です。
非同期 Bean の例:
ArrayList<WorkItem> items = new ArrayList<WorkItem>(3);
items.add(abWorkManager.startWork(new PrimeFinderWork(20)));
items.add(abWorkManager.startWork(new PrimeFinderWork(50)));
items.add(abWorkManager.startWork(new PrimeFinderWork(80)));
boolean anyCompleted = abWorkManager.join(items, WorkManager.JOIN_OR, TIMEOUT_MS);
long prime = -1;
for (WorkItem workItem : items) {
if (workItem.getStatus() == WorkEvent.WORK_COMPLETED) {
PrimeFinderWork work = (PrimeFinderWork) workItem.getResult();
prime = work.getPrimeNumber();
} else
((Work) workItem.getEventTrigger(Work.class)).release();
}
CommonJ の例:
List<PrimeFinderWork> workList = Arrays.asList(
new PrimeFinderWork(20),
new PrimeFinderWork(50),
new PrimeFinderWork(80));
List<WorkItem> items = new ArrayList<WorkItem>(workList.size());
for (PrimeFinderWork work : workList)
items.add(cjWorkManager.schedule(work));
Collection<WorkItem> completedItems = cjWorkManager.waitForAny(items, TIMEOUT_MS);
long prime = -1;
for (int i = 0; i < items.size(); i++) {
WorkItem workItem = items.get(i);
if (completedItems.contains(workItem)) {
PrimeFinderWork work = (PrimeFinderWork) workItem.getResult();
prime = work.getPrimeNumber();
} else
workList.get(i).release();
}
Concurrency Utilities for Java EE example:
List<PrimeFinderTask> tasks = Arrays.asList(
new PrimeFinderTask(20),
new PrimeFinderTask(50),
new PrimeFinderTask(80));
long prime = executor.invokeAny(tasks, TIMEOUT_MS, TimeUnit.MILLISECONDS);
タスクの失敗時の原因例外の取得
3 つすべてのプログラミング・モデルで、タスクの実行が失敗した時に原因例外を取得する方法を提供しています。これは、リスナーを使用するか (例はこのページの後ろにあります)、WorkItem または Future からタスク結果を取得することで行うことができます。WorkException または ExecutionException が生成されます。これに、原因として元の例外が含まれます。
非同期 Bean の例:
boolean isLongRunning = false;
WorkItem workItem = abWorkManager.startWork(
new DBInsertWorkAB("KADC", "Wadena Municipal Airport"),
isLongRunning);
Throwable exception = null;
ArrayList<WorkItem> items = new ArrayList<WorkItem>(1);
items.add(workItem);
if (abWorkManager.join(items, WorkManager.JOIN_AND, TIMEOUT_MS))
try {
DBInsertWorkAB work = (DBInsertWorkAB) workItem.getResult();
int numUpdates = work.getResult();
} catch (WorkException x) {
exception = x.getCause();
}
CommonJ の例:
boolean isLongRunning = false;
DBInsertWorkCJ work = new DBInsertWorkCJ("KBDH", "Willmar Municipal Airport");
work.setDaemon(isLongRunning);
WorkItem workItem = cjWorkManager.schedule(work);
Throwable exception = null;
if (cjWorkManager.waitForAll(Collections.singleton(workItem), TIMEOUT_MS))
try {
work = (DBInsertWorkCJ) workItem.getResult();
int numUpdates = work.getResult();
} catch (WorkException x) {
exception = x.getCause();
}
Concurrency Utilities for Java EE example:
boolean isLongRunning = false;
Callable<Integer> task = ManagedExecutors.managedTask(
new DBInsertTask("KACQ", "Waseca Municipal Airport"),
Collections.singletonMap(ManagedTask.LONGRUNNING_HINT,
Boolean.toString(isLongRunning)),
null);
Future<Integer> future = executor.submit(task);
Throwable exception = null;
try {
int numUpdates = future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
} catch (ExecutionException x) {
exception = x.getCause();
}
間隔の後で実行するワンタイム・タスクのスケジューリング
3 つすべてのプログラミング・モデルで、将来のどこかの時点においてプールされたスレッドで実行して結果を取得するための基本タスクをスケジュールする方法を提供しています。
非同期 Bean の例:
AsynchScope asynchScope = abWorkManager.findOrCreateAsynchScope("MyScope");
AlarmManager alarmManager = asynchScope.getAlarmManager();
Alarm alarm = alarmManager.create(
new DBInsertAlarmListener(),
new String[] { "MSP", "Minneapolis-Saint Paul International Airport"},
(int) TimeUnit.SECONDS.toMillis(1));
DBInsertAlarmListener alarmListener = (DBInsertAlarmListener) alarm.getAlarmListener();
// Poll for result to appear
for (long start = System.nanoTime();
alarmListener.getResult() < 0 && System.nanoTime() - start < TIMEOUT_NS;
Thread.sleep(200)) ;
int numUpdates = alarmListener.getResult();
CommonJ の例:
Timer timer = timerManager.schedule(
new DBInsertTimerListener("STC", "Saint Cloud Regional Airport"),
TimeUnit.SECONDS.toMillis(1));
DBInsertTimerListener timerListener = (DBInsertTimerListener) timer.getTimerListener();
// Poll for result to appear
for (long start = System.nanoTime();
timerListener.getResult() < 0 && System.nanoTime() - start < TIMEOUT_NS;
Thread.sleep(200)) ;
int numUpdates = timerListener.getResult();
Concurrency Utilities for Java EE example:
ScheduledFuture<Integer> future = executor.schedule(
new DBInsertTask("RST", "Rochester International Airport"),
1,
TimeUnit.SECONDS);
int numUpdates = future.get(TIMEOUT_NS, TimeUnit.NANOSECONDS);
固定レートでの繰り返しタスクのスケジューリングと次の実行までの間隔の照会
CommonJ および Concurrency Utilities for Java EE のプログラミング・モデルでは、繰り返しタスクを固定レートで (毎正時など) 実行するようにスケジュールする方法を提供しています。リアルタイム・スケジューリングは保証されていません。このタスクは、即時ではなく、この時刻の後の任意の時点で開始できます。CommonJ および Concurrency Utilities for Java EE のプログラミング・モデルには、次回の実行までの遅延を計算する便利メソッドもあります。
CommonJ の例:
Timer timer = timerManager.scheduleAtFixedRate(
new PrimeFinderTimerListener(120),
TimeUnit.MINUTES.toMillis(90),
TimeUnit.MINUTES.toMillis(30));
long nextExecTime = timer.getScheduledExecutionTime();
long delay = TimeUnit.MILLISECONDS.toSeconds(
nextExecTime - System.currentTimeMillis());
Concurrency Utilities for Java EE example:
ScheduledFuture<?> future = executor.scheduleAtFixedRate(
new PrimeFinderRunnable(120), 90, 30, TimeUnit.MINUTES);
long delay = future.getDelay(TimeUnit.SECONDS);
タスクの実行間に固定遅延を指定する繰り返しタスクのスケジューリングとそのタスクの取り消し
CommonJ および Concurrency Utilities for Java EE のプログラミング・モデルでは、ある実行の終了から次の開始までの間を固定間隔で実行するように、繰り返しタスクをスケジュールする方法を提供しています。リアルタイム・スケジューリングは保証されていません。このタスクは、即時ではなく、この時刻の後の任意の時点で開始できます。非同期 Bean には、Alarm のリセット・メソッドがあり、これを使用することで同じ動作を達成できます。3 つすべてのプログラミング・モデルで、スケジュール済みのタスクの後続の実行を取り消して、タスクが開始されないようにする方法を提供しています。
非同期 Bean の例:
AsynchScope asynchScope = abWorkManager.findOrCreateAsynchScope("MyScope");
AlarmManager alarmManager = asynchScope.getAlarmManager();
Alarm alarm = alarmManager.create(
new PrimeFinderAlarmListener(90), 50, 10);
// ... eventually cancel the alarm
alarm.cancel();
CommonJ の例:
Timer timer = timerManager.schedule(
new PrimeFinderTimerListener(90), 50, 50);
// ... eventually cancel the timer
timer.cancel();
Concurrency Utilities for Java EE example:
ScheduledFuture<?> future = executor.scheduleWithFixedDelay(
new PrimeFinderRunnable(90), 50, 50, TimeUnit.MILLISECONDS);
// ... eventually cancel the task
future.cancel(false);
さまざまな間隔で実行する繰り返しタスクのスケジューリング
3 つすべてのプログラミング・モデルで、実行ごとに、繰り返しタスク後から次の繰り返しまでの間隔を計算できます。非同期 Bean には、アラームのリセット・メソッドがあります。Concurrency Utilities for Java EE を使用すると、次の実行時間を計算するトリガーに接続できます。CommonJ にはこのどちらもありませんが、CommonJ およびその他のプログラミング・モデルでは、前回の実行の完了時にタスクのスケジュールを変更することができます。以下の例では、各実行の前に異なる遅延を指定して、正確に 4 回実行するようにタスクをスケジュールします。次回の実行のリセット、スケジュール変更、または計算を行うコードは、このセクションに含まれる Trigger と、基本タスクの実装セクションに含まれている AlarmListener および TimerListener の各実装にあります。
非同期 Bean の例:
int initialDelay = 50;
int[] subsequentDelays = new int[] { 40, 80, 70 };
AsynchScope asynchScope = abWorkManager.findOrCreateAsynchScope("MyScope");
AlarmManager alarmManager = asynchScope.getAlarmManager();
Alarm alarm = alarmManager.create(
new PrimeFinderAlarmListener(60),
subsequentDelays,
initialDelay);
Thread.sleep(5000);
PrimeFinderAlarmListener alarmListener =
(PrimeFinderAlarmListener) alarm.getAlarmListener();
long prime = alarmListener.getPrimeNumber();
CommonJ の例:
long initialDelay = 50;
long [] subsequentDelays = new long[] { 40, 80, 70 };
Timer timer = timerManager.schedule(
new PrimeFinderTimerListener(60, subsequentDelays),
initialDelay);
Thread.sleep(5000);
PrimeFinderTimerListener timerListener = (PrimeFinderTimerListener) timer.getTimerListener();
long prime = timerListener.getPrimeNumber();
Concurrency Utilities for Java EE example:
ScheduledFuture<Long> future = executor.schedule(
new PrimeFinderTask(60),
new DelayTrigger(50, 40, 80, 70));
Thread.sleep(5000);
long prime = future.get();
public class DelayTrigger implements Trigger {
private int count;
private long[] delays;
volatile boolean isSuspended;
public DelayTrigger(long... delays) {
this.delays = delays;
}
public Date getNextRunTime(LastExecution previousExecution, Date taskScheduledTime) {
if (delays.length > count)
return new Date(System.currentTimeMillis() + delays[count++]);
else
return null;
}
public boolean skipRun(LastExecution previousExecution, Date scheduledRunTime) {
return isSuspended;
}
}
タスクの実行の中断および再開
CommonJ TimerManager は、タスクの実行を中断および再開するインターフェースを提供しています。Concurrency Utilities for Java EE は、プラグ可能 Trigger メカニズムの skipRun メソッドによって、より細分化されたベースでこの機能を提供します。これをサポートする ための Trigger が実装されている場合は、単一の Trigger インスタンスを任意数のタスクに提供することができます。以下の例では、単一タスクのスケジューリングに使用するように、Trigger が作成されています。
CommonJ の例:
Timer timer = timerManager.schedule(
new PrimeFinderTimerListener(100),
new Date(System.currentTimeMillis() + TimeUnit.DAYS.toMillis(5)));
timerManager.suspend();
// ... resume at a later point
if (timerManager.isSuspending() || timerManager.isSuspended())
timerManager.resume();
Concurrency Utilities for Java EE example:
DelayTrigger trigger = new DelayTrigger(
System.currentTimeMillis() + TimeUnit.DAYS.toMillis(5));
ScheduledFuture<Long> future = executor.schedule(
new PrimeFinderTask(100), trigger);
trigger.isSuspended = true;
// ... resume at a later point
if (trigger.isSuspended)
trigger.isSuspended = false;
後続のタスク実行の停止
非同期 Bean は、AsynchScope を破棄する方法を提供しています。これは、AlarmManagers がそのスコープ内に作成したすべてのアラームを取り消します。CommonJ TimerManager には、後続の実行の開始を止め、実行中のすべてのタスクが停止するまで待機するインターフェースがあります。これが可能なのは、TimerManager の各検索で新規インスタンスが作成され、そのインスタンスをその他のインスタンスとは無関係に停止できるためです。Concurrency Utilities for Java EE では、同一の ManagedScheduledExecutorService が、検索やライフサイクル操作 (shutdown など) 全体で共有され、isTerminated および awaitTermination は許可されません (仕様に準拠)。ただし、Concurrency Utilities for Java EE では、管理対象外の ScheduledExecutorService に ManagedThreadFactory を指定することによって同様の動作を達成できます。
非同期 Bean の例:
alarmManager.create(
new PrimeFinderAlarmListener(100),
null,
(int) TimeUnit.HOURS.toMillis(1));
alarmManager.create(
new PrimeFinderAlarmListener(200),
null,
(int) TimeUnit.HOURS.toMillis(2));
// ... eventually destroy the asynch scope to cancel all alarms
asynchScope.destroy();
CommonJ の例:
TimerManager timerManager = (TimerManager) new InitialContext().lookup(
"java:comp/env/tm/default");
timerManager.schedule(
new PrimeFinderTimerListener(100),
new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(1)));
timerManager.schedule(
new PrimeFinderTimerListener(200),
new Date(System.currentTimeMillis() + TimeUnit.HOURS.toMillis(2)));
// ... eventually stop the timer manager
timerManager.stop();
if (!timerManager.isStopped())
timerManager.waitForStop(TIMEOUT_MS);
Concurrency Utilities for Java EE example:
ScheduledExecutorService executor =
Executors.newScheduledThreadPool(1, threadFactory);
executor.schedule(new PrimeFinderTask(100), 1, TimeUnit.HOURS);
executor.schedule(new PrimeFinderTask(200), 2, TimeUnit.HOURS);
// .. eventually shut down the executor
executor.shutdown();
if (!executor.isTerminated())
executor.awaitTermination(TIMEOUT_MS, TimeUnit.MILLISECONDS);
コンテキスト・プロキシーの構成
非同期 Bean および Concurrency Utilities for Java EE のプログラミング・モデルでは、コンテキスト・プロキシーを構成することができます。スレッド・コンテキストは、コンテキスト・プロキシーを作成するスレッドから取り込まれ、そのスレッド内に保管されます。スレッド・コンテキストは、インターフェース・メソッドがプロキシー上で呼び出されるときに実行スレッドに自動的に適用され、その後実行スレッドから削除されます。非同期 Bean では、EventSource によって、この機能を提供します。Concurrency Utilities for Java EE では、ContextService によって、この機能を提供します。
非同期 Bean の例:
EventSource eventSource = abWorkManager.createEventSource();
eventSource.addListener(new DBWriterImpl());
DBWriter dbWriter = (DBWriter) eventSource.getEventTrigger(DBWriter.class);
// Can invoke interface methods from any thread...
int numUpdates = dbWriter.exec(
"INSERT INTO AIRPORTS VALUES(?,?)", "AIT", "Aitkin Municipal Airport");
Concurrency Utilities for Java EE example:
DBWriter dbWriter = contextService.createContextualProxy(
new DBWriterImpl(), DBWriter.class);
// Can invoke interface methods from any thread...
int numUpdates = dbWriter.exec(
"INSERT INTO AIRPORTS VALUES(?,?)", "AXN", "Alexandria Municipal Airport");
実行スレッドのトランザクションで実行する複数インスタンス用のコンテキスト・プロキシーの構成
非同期 Bean および Concurrency Utilities for Java EE はどちらも、実行のスレッドのトランザクション内でコンテキスト・プロキシーのインターフェース・メソッドを実行するかどうか、あるいは現行のトランザクションを中断して、そのメソッド中はクリーンなトランザクション・コンテキストを適用するかどうかを指定できます。非同期 Bean のもう 1 つの機能は、複数のリスナー・インスタンスを単一のプロキシーでトリガーできることです。しかし、Concurrency Utilities for Java EE にはこの機能が欠けており、複数インスタンスに委任する単一のラッパー・リスナーを作成することで、類似の動作を実現できます。以下の例では、単一プロキシーを呼び出し、その 呼び出しスレッドのすべてのトランザクション下で、複数のリスナー・インスタンスをトリガーします。
非同期 Bean の例:
boolean runInSameTran = true;
EventSource eventSource = abWorkManager.createEventSource();
eventSource.addListener(new DBInsertTask("MKT", "Mankato Regional Airport"));
eventSource.addListener(new DBInsertTask("ULM", "New Ulm Municipal Airport"));
eventSource.addListener(new DBInsertTask("OWA", "Owatonna Degner Regional Airport"));
Callable<?> eventTrigger = (Callable<?>) eventSource.getEventTrigger(
Callable.class, runInSameTran);
// Can invoke interface methods from any thread...
tran.begin();
try {
eventTrigger.call();
} finally {
tran.commit();
}
Concurrency Utilities for Java EE example:
Callable<?> eventTrigger = contextService.createContextualProxy(
new Callable<Void>() {
@Override
public Void call() throws Exception {
new DBInsertTask("FFM", "Fergus Falls Municipal Airport").call();
new DBInsertTask("ONA", "Winona Municipal Airport").call();
new DBInsertTask("OTG", "Worthington Municipal Airport").call();
return null;
}
},
Collections.singletonMap(ManagedTask.TRANSACTION,
ManagedTask.USE_TRANSACTION_OF_EXECUTION_THREAD),
Callable.class);
// Can invoke interface methods from any thread...
tran.begin();
try {
eventTrigger.call();
} finally {
tran.commit();
}
呼び出しスレッドの据え置き実行用のコンテキスト・タスクの作成
非同期 Bean では WorkWithExecutionContext を作成できます。これは基本的に、タスク用のシリアライズ可能コンテキスト・プロキシーであり、後で WorkManager に実行依頼して、作成元スレッドのスレッド・コンテキスト下で実行することができます。呼び出しスレッド (WorkManager の doWork メソッド) で実行する場合は、Concurrency Utilities for Java EE のコンテキスト・プロキシーで類似の動作を実行できます。
非同期 Bean の例:
WorkWithExecutionContext contextualWork = abWorkManager.create(
new DBInsertWorkAB("BJI", "Bemidji Regional Airport"));
// Can run the contextual work on any thread...
abWorkManager.doWork(contextualWork);
DBInsertWorkAB work = (DBInsertWorkAB) contextualWork.getWork();
int numUpdates = work.getResult();
Concurrency Utilities for Java EE example:
Callable<Integer> contextualTask = contextService.createContextualProxy(
new DBInsertTask("BRD", "Brainerd Lakes Regional Airport"),
Callable.class);
// Can run the contextual proxy on any thread...
int numUpdates = contextualTask.call();
プールされたスレッドの据え置き実行用のコンテキスト・タスクの作成
非同期 Bean では WorkWithExecutionContext を作成できます。これは基本的に、タスク用のシリアライズ可能コンテキスト・プロキシーであり、後で WorkManager に実行依頼して、作成元スレッドのスレッド・コンテキスト下で実行することができます。プールされたスレッド (WorkManager の startWork メソッド) で実行する場合は、管理対象 executor にタスクのコンテキスト・プロキシーを実行依頼することで、Concurrency Utilities for Java EE で類似の動作を実行することができます。この場合、スレッド・コンテキストの取り込みと伝搬が重複するため、効率は悪くなります。
非同期 Bean の例:
WorkWithExecutionContext contextualWork = abWorkManager.create(
new DBInsertWorkAB("ELO", "Ely Municipal Airport"));
WorkItem workItem = abWorkManager.startWork(contextualWork);
ArrayList<WorkItem> items = new ArrayList<WorkItem>(1);
items.add(workItem);
if (abWorkManager.join(items, WorkManager.JOIN_AND, TIMEOUT_MS)) {
DBInsertWorkAB work = (DBInsertWorkAB) workItem.getResult();
int numUpdates = work.getResult();
}
Concurrency Utilities for Java EE example:
Callable<Integer> contextualTask = contextService.createContextualProxy(
new DBInsertTask("EVM", "Eveleth-Virginia Municipal Airport"),
Callable.class);
Future<Integer> future = executor.submit(contextualTask);
int numUpdates = future.get(TIMEOUT_MS, TimeUnit.MILLISECONDS);
据え置き実行用のコンテキスト・タスクの追加オプション
据え置き実行用のタスクを実行依頼するときに、必要に応じてリスナーおよび開始タイムアウトを割り当てたり、長時間の実行が予想されるかどうかを示したりすることができます。開始タイムアウトは、非同期 Bean ではパラメーターとしてのみ使用できますが、CommonJ および Concurrency Utilities for Java EE の場合は、WorkListener または ManagedTaskListener の内部で開始タイムアウトを実装することができます。
非同期 Bean の例:
long startTimeout = TIMEOUT_MS;
boolean isLongRunning = true;
WorkWithExecutionContext contextualWork = abWorkManager.create(
new DBInsertWorkAB("FRM", "Fairmont Municipal Airport"));
WorkItem workItem = abWorkManager.startWork(
contextualWork, startTimeout, new WorkListenerAB(), isLongRunning);
ArrayList<WorkItem> items = new ArrayList<WorkItem>(1);
items.add(workItem);
if (abWorkManager.join(items, WorkManager.JOIN_AND, Integer.MAX_VALUE)) {
DBInsertWorkAB work = (DBInsertWorkAB) workItem.getResult();
int numUpdates = work.getResult();
}
Concurrency Utilities for Java EE example:
long startTimeout = TIMEOUT_MS;
boolean isLongRunning = true;
Callable<Integer> contextualTask = contextService.createContextualProxy(
new DBInsertTask("FBL", "Faribault Municipal Airport"),
Callable.class);
contextualTask = ManagedExecutors.managedTask(
contextualTask,
Collections.singletonMap(ManagedTask.LONGRUNNING_HINT,
Boolean.toString(isLongRunning)),
new TaskListener(startTimeout));
Future<Integer> future = executor.submit(contextualTask);
int numUpdates = future.get();
サブシステム・モニター
非同期 Bean は、アプリケーションやその他の成果物全体を調整して可用性をモニターするメカニズムとして、SubsystemMonitor および SubsystemMonitorManager を提供しています。Concurrency Utilities for Java EE には、これと同等の機能はありません。この機能の置き換えが必要な場合は、他のすべてのアプリケーションによって SubsystemMonitorManager と同等の機能を果たすと認識されるアプリケーションを実装することができます。
イベント処理
非同期 Bean は、AlarmManager、AsynchScope、EventSource、SubsystemMonitor、WorkManager、および Work で発生する各種のイベントに対してリスナーを登録する機能を提供しています。ほとんどの場合、これらのイベントに直接対応する機能は Concurrency Utilities for Java EE にはありません。イベントおよび通知のための独自のメカニズムを実装するのは、アプリケーションの責任になります。いくつかのケースでは、Concurrency Utilities for Java EE にも同様の機能があります。場合によっては、ManagedTaskListener を使用できる可能性があります。このリスナーは、AlarmManagerEvents および WorkEvents の代わりに、より細分化されたベースで登録されます (タスクの実行依頼時)。
コンテキスト・プロキシーの呼び出し中に発生する障害のイベント処理
非同期 Bean では、コンテキスト・プロキシーを使用して操作を呼び出し、その操作で宣言された例外が発生した場合、呼び出し側に例外が報告されません。代わりに、listenerExceptionThrown イベントで、例外が EventSourceEvents リスナーに報告されます。Concurrency Utilities for Java EE では、呼び出し側が例外をキャッチして処理することができます。
非同期 Bean の例:
EventSource eventSource = abWorkManager.createEventSource();
eventSource.addListener(new DBWriterImpl());
eventSource.addListener(new EventSourceEvents() {
public void listenerCountChanged(
EventSource es, int oldCount, int newCount) {}
public void listenerExceptionThrown(
EventSource es, Object listener,
String methodName, Throwable exception) {
listenerException.set(exception);
}
public void unexpectedException(
EventSource es, Object runnable, Throwable exception) {}
});
DBWriter dbWriter = (DBWriter) eventSource.getEventTrigger(DBWriter.class);
// Can invoke interface methods from any thread...
try {
dbWriter.exec(
"INSERT INTO AIRPORTS VALUES(?,?)", "KAUM", "Austin Municipal Airport");
} catch (Exception x) {
// expecting this to fail
}
Throwable exception = listenerException.get();
Concurrency Utilities for Java EE example:
DBWriter dbWriter = contextService.createContextualProxy(
new DBWriterImpl(), DBWriter.class);
// Can invoke interface methods from any thread...
SQLException exception = null;
try {
dbWriter.exec(
"INSERT INTO AIRPORTS VALUES(?,?)", "KSBU", "Blue Earth Municipal Airport");
} catch (SQLException x) {
exception = x;
}