除了我更一般的并发答案之外,另一个选择是使用 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 { ... }
JavaProxy
需要两件事才能工作:接口(得到了)和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
. 但是,您始终可以将其与回调或事件分派结合使用,以从这些东西中获取结果。请记住,这就像提交作业并离开它们,因此如果不实施某种回调系统,您将无法获得任何回报。
再说一句:如果您想确保所有对象都被包装,那么您需要一个工厂来创建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
包设为私有,这样包外的任何人都无法通过public
从类声明中删除来访问它:
class FooImpl implements Foo { ... }
你去吧。这是包装对象以通过简单的方法调用进行后台调用的好方法。此代码将doSomething
在后台执行并立即打印给定的语句:
Foo myFoo = FooFactory.createFoo();
myFoo.doSomething();
System.out.println("Prints immediately without waiting");
与我的其他答案相比,这更接近您想要的,但由于返回类型要求而受到更多限制。