私のより一般的な並行性の答え以外のもう 1 つのオプションは、Java のProxy
クラスを使用することです。
クラスがあり、Foo
それをラップして、バックグラウンド スレッドですべてのメソッドを実行するとします。doSomething
実行に時間がかかるメソッドがあります。
public class Foo {
public void doSomething() {
// network I/O or something
Thread.sleep(5000);
}
}
を使用するProxy
には、インターフェースが必要です。(このチュートリアルを参照してください。) 以下からインターフェースを作成しますFoo
。
public interface Foo {
void doSomething();
}
古いFoo
クラスに新しいインターフェイスを実装させます。
public class FooImpl implements Foo { ... }
Java がProxy
機能するためには、インターフェイス (取得済み) とInvocationHandler
. これInvocationHandler
は、バックグラウンド スレッドでメソッドを実行するものです。
Proxy
のインスタンスで を作成する方法は次のFooImpl
とおりです。
Foo foo = new FooImpl();
foo = (Foo) Proxy.newInstance(
System.getClassLoader(),
new Class[] { Foo.class },
new BackgroundInvocationHandler(foo));
本当に簡単です。あとは次のように書くだけBackgroundInvocationHandler
です:
public class BackgroundInvocationHandler implements InvocationHandler {
private static final int THREAD_COUNT = 20;
private static final Executor executor = Executors.newFixedThreadPool(THREAD_COUNT);
private final Object obj;
public BackgroundInvocationHandler(Object obj) {
this.obj = obj;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable {
executor.execute(new MethodInvoker(m, args, obj));
return null;
}
private static class MethodInvoker implements Runnable {
private final Method m;
private final Object[] args;
private final Object target;
MethodInvoker(Method m, Object[] args, Object target) {
this.m = m;
this.args = args;
this.target = target;
}
@Override
public void run() {
try {
m.invoke(target, args);
} catch(Exception e) {
// TODO log me
}
}
}
}
おそらく予想通りだと思いますが、これは を返すメソッドでのみうまく機能しますvoid
。Proxy
インスタンスは、null
ではないものを返すものに対して返されますvoid
。ただし、これをコールバックまたはイベントディスパッチといつでも組み合わせて、このようなものから結果を取得できます。これは、ジョブを送信して終了するようなものであるため、何らかのコールバック システムを実装しないと何も返されないことに注意してください。
もう 1 つ注意してください。すべてのオブジェクトがラップされていることを確認したい場合は、オブジェクトを作成するためのファクトリが必要になりFoo
ます。
public class FooFactory {
public static Foo createFoo() {
Foo foo = new FooImpl();
foo = (Foo) Proxy.newInstance(
System.getClassLoader(),
new Class[] { Foo.class },
new BackgroundInvocationHandler(foo));
return foo;
}
}
そして、クラス宣言からFooImpl
削除することにより、パッケージの外部の誰もアクセスできないように、package-privateを作成します。public
class FooImpl implements Foo { ... }
そして、そこに行きます。これは、単純なメソッド呼び出しでバックグラウンド呼び出しを行うためにオブジェクトをラップする良い方法です。このコードはdoSomething
バックグラウンドで実行され、指定されたステートメントをすぐに出力します。
Foo myFoo = FooFactory.createFoo();
myFoo.doSomething();
System.out.println("Prints immediately without waiting");
これは、私の他の回答と比較して、あなたが望んでいたものにはるかに近いですが、戻り値の型の要件により、より制限されています。