前言
java中可以通过接口的多态特性来达到调用外部实现类的接口方法的效果,比如我们经常使用的框架,驱动程序等,都会定义许多的接口,向外部暴露统一的接口方法,内部则调用这些接口来达到业务逻辑的实现.但是很多的调用采用的都是同步的机制, 也就是说当我们向某个对象传递了某个interface的实现类,必须等到方法内部调用这个实现类的方法完毕后才会返回,否则余下的代码就会一直阻塞.而有时候我们需要在这种调用的时候不阻塞之后的代码执行,这时候就需要用到异步调用了.
系统中有一个Worker类, 其在工作完成时需要调用外部传入的handler来处理一些耗时操作
public class Worker {
static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public void startWork(int i, AsyncHandler handler) {
/*
* code......
* 一些业务操作完成,需要调用外部传入的handler来完成耗时操作
*/
System.out.println("worker-" + i + " start Work @" + format.format(new Date()));
handler.callback();//这里直接调用handler定义的模板方法callback
}
}
这里我们定义一个
abstract class AsyncHandler :
abstract class AsyncHandler {
public void callback() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
//这里通过休眠一秒子线程来达到模拟handler耗时操作
Thread.sleep(1000 * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
handle();
}
});
thread.start();
}
abstract public void handle();
}
可以看到该AsyncHandler 向外部暴露了一个handler抽象方法,该抽象方法可能在执行时需要耗费一定时间,这时候我们可以在内部通过一个callback方法来产生一个新的线程去调用handler方法(通过sleep方法来模拟耗时操作)
main方法中Worker实例开始工作
public static void main(String[] args) {
//一个匿名的AsyncHandler 实现类,具体的实现为打印一句话
AsyncHandler handler = new AsyncHandler() {
@Override
public void handle() {
System.out.println("handler start @ " + format.format(new Date()));
}
};
Worker worker = new Worker();
worker.startWork(1, handler);
System.out.println("main thread ending @" + format.format(new Date()));
}
运行结果
worker-1 start Work @2015-11-29 16:23:46.318
main thread ending @2015-11-29 16:23:46.319
handler start @2015-11-29 16:23:48.319
可以从结果中看到主线程在worker启动startWork一毫秒之后就又接着运行余下的代码了,并没有阻塞主线程等到worker的工作全部完成才接着执行.handler在经过两秒的耗时处理后返回,整个程序顺利结束.
当然,这个机制还有另一个变种
通过在匿名AsyncHandler的handler方法中直接产生子线程来完成耗时工作,Worker在startWork方法中直接调用hanler的handle方法也可以达到一步调用:
public class Worker2 {
static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
public void startWork(int i, AsyncHandler handler) {
/*
* code......
* 一些业务操作完成,需要调用外部传入的handler来完成耗时操作
*/
System.out.println("worker-" + i + " start Work @" + format.format(new Date()));
handler.handle();
}
public static void main(String[] args) {
AsyncHandler handler = new AsyncHandler() {
@Override
public void handle() {
new Thread(new Runnable() {
@Override
public void run() {
try {
//这里通过休眠一秒子线程来达到模拟handler耗时操作
Thread.sleep(1000 * 2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("handler start @ " + format.format(new Date()));
}
}).start();
}
};
Worker2 worker = new Worker2();
worker.startWork(1, handler);
System.out.println("main thread ending @" + format.format(new Date()));
}
}
运行结果
worker-1 start Work @2015-11-29 16:43:35.671
main thread ending @2015-11-29 16:43:35.672
handler start @ 2015-11-29 16:43:37.672
从运行结果来看和前一种实现一样,都实现了异步调用的功能,但是后者需要在实现handle方法时显示开启子线程,这样一来,handle的实现就需要调用者除了考虑业务外还需要考虑子线程的实现,从而整个代码可读性变差.前一种方案将内部实现异步透明化,调用者只需要考虑handler的handle方法内部具体实现什么业务即可,整个代码可读性也提高了.