带你一步步实现线程池异步回调
2016-03-10 23:26
555 查看
1.字面意义上的回调
字面意思上理解回调,就是A调用B,B回过头来再调用A,即是回调.既然是这样,当然就要求A中有B,B中有A.如下:
2.面向对象的回调
上面的写法中,B的对象只在方法中被传递了.实际上,这个B对象后来又调用了A中的方法,它的作用应该不止局限在一个方法中,而应该是A的一个部分.也就是,上面的写法不够"面向对象",让我们来改造一下:
3.面向接口的回调
上面的两个例子,估计没人会承认也是回调吧.因为并没什么卵用.不过这个流程对于理解回调是很重要的.其实回调真正有用的地方,在于它的"预测"能力.
我们扩展想象一下.假设上面例子中的B,为A提供了很多服务之后突然觉醒,想为更多的对象提供服务,这样一来,B就变成了Server.而且还要制定规则.规则是什么呢,就是要Server提供服务可以,对方一定要有一个recvAnswer接口供Server调用才行,这样Server才能把结果传回给Client.具体如何制定规则呢?通过Interface.如下:
注意,接口IClient实际上应该是属于Server端的,它是由Server制定的,需要Client来实现的接口,虽然看上去它跟Client很近.
为什么说有"预测"能力呢?想象另一个场景.Server现在是一个底层服务,这个底层服务知道迟早有一天会有高层服务来讨要数据,但是数据如何向上传递呢?底层可以承诺,只有你实现IClient接口,我就会调用其中的recvAnswer方法,把数据传上来.现在底层也可以调用高层的方法,算是有"预测"能力吧?
4.异步回调
上面的调用都是同步的.假设Server计算结果需要较长的时间,你一定希望它能在一个单独的线程中被执行,这是就可以把ask方法的调用用线程包装一下:
5.线程池异步回调
每次建立新的线程耗费资源巨大,为了重用线程,使用线程池管理异步调用,这时候就要求Client不仅要实现IClient接口,还要同时是一个任务,才能被线程池执行,如下:
至此,我们就实现了线程池异步回调.
字面意思上理解回调,就是A调用B,B回过头来再调用A,即是回调.既然是这样,当然就要求A中有B,B中有A.如下:
class A { public void ask(final B b, final String question) { b.answer(this, question); } public void processResult(final String answer) { System.out.println(answer); } } class B { public void answer(final A a, final String question) { if (question.equals("What is the answer to life, the universe and everything?")) { a.processResult("42"); } } } public class SyncObjectCallback { public static void main(final String[] args) { B b = new B(); A a = new A(); a.ask(b, "What is the answer to life, the universe and everything?"); } }
2.面向对象的回调
上面的写法中,B的对象只在方法中被传递了.实际上,这个B对象后来又调用了A中的方法,它的作用应该不止局限在一个方法中,而应该是A的一个部分.也就是,上面的写法不够"面向对象",让我们来改造一下:
class A { private final B b; public A(final B b) { this.b = b; } public void ask(final String question) { this.b.answer(this, question); } public void processResult(final String answer) { System.out.println(answer); } } class B { public void answer(final A a, final String question) { if (question.equals("What is the answer to life, the universe and everything?")) { a.processResult("42"); } } } public class SyncOOCallback { public static void main(final String[] args) { B b = new B(); A a = new A(b); a.ask("What is the answer to life, the universe and everything?"); } }
3.面向接口的回调
上面的两个例子,估计没人会承认也是回调吧.因为并没什么卵用.不过这个流程对于理解回调是很重要的.其实回调真正有用的地方,在于它的"预测"能力.
我们扩展想象一下.假设上面例子中的B,为A提供了很多服务之后突然觉醒,想为更多的对象提供服务,这样一来,B就变成了Server.而且还要制定规则.规则是什么呢,就是要Server提供服务可以,对方一定要有一个recvAnswer接口供Server调用才行,这样Server才能把结果传回给Client.具体如何制定规则呢?通过Interface.如下:
public interface IClient { void recvAnswer(String answer); }
public class Server { public void answer(final IClient client, final String question) { if (question.equals("What is the answer to life, the universe and everything?")) { calclating(); client.recvAnswer("42"); } } private void calclating() { try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } } }
public class ClientSync implements IClient { private final Server server; public ClientSync(final Server server) { this.server = server; } public void ask(final String question) { this.server.answer(this, question); } @Override public void recvAnswer(final String answer) { System.out.println(answer); } }
public class SyncInterfaceCallback { public static void main(final String[] args) { Server server = new Server(); ClientSync client = new ClientSync(server); client.ask("What is the answer to life, the universe and everything?"); } }
注意,接口IClient实际上应该是属于Server端的,它是由Server制定的,需要Client来实现的接口,虽然看上去它跟Client很近.
为什么说有"预测"能力呢?想象另一个场景.Server现在是一个底层服务,这个底层服务知道迟早有一天会有高层服务来讨要数据,但是数据如何向上传递呢?底层可以承诺,只有你实现IClient接口,我就会调用其中的recvAnswer方法,把数据传上来.现在底层也可以调用高层的方法,算是有"预测"能力吧?
4.异步回调
上面的调用都是同步的.假设Server计算结果需要较长的时间,你一定希望它能在一个单独的线程中被执行,这是就可以把ask方法的调用用线程包装一下:
public class ClientAsync implements IClient { private final Server server; public ClientAsync(final Server server) { this.server = server; } public void ask(final String question) { new Thread(new Runnable() { @Override public void run() { server.answer(ClientAsync.this, question); } }).start(); } @Override public void recvAnswer(final String answer) { System.out.println(answer); } }
public class AsyncInterfaceCallback { public static void main(final String[] args) { Server server = new Server(); ClientAsync client = new ClientAsync(server); client.ask("What is the answer to life, the universe and everything?"); System.out.println("asked ! waiting for the answer..."); } }
5.线程池异步回调
每次建立新的线程耗费资源巨大,为了重用线程,使用线程池管理异步调用,这时候就要求Client不仅要实现IClient接口,还要同时是一个任务,才能被线程池执行,如下:
public class ClientRunnable implements IClient, Runnable { private final Server server; private final String question; private final int id; public ClientRunnable(final Server server, final String question, final int id) { this.server = server; this.question = question; this.id = id; } @Override public void recvAnswer(final String answer) { System.out.println("clinet " + this.id + " got answer: " + answer); } @Override public void run() { server.answer(ClientRunnable.this, this.question); } }
public class ThreadpoolCallback { public static void main(final String[] args) { ExecutorService es = Executors.newCachedThreadPool(); Server server = new Server(); for (int i = 0; i < 100; i++) { ClientRunnable cr = new ClientRunnable(server, "What is the answer to life, the universe and everything?", i); es.execute(cr); System.out.println("client " + i + " asked !"); } es.shutdown(); } }
至此,我们就实现了线程池异步回调.
相关文章推荐
- 你真的看懂R中的stem函数了吗?
- jdbc批量插入实现大批量数据快速插入
- 专注做事,竟然也成为我们的稀缺能力 [20160310]
- 读《我是IT小小鸟》有感
- Win8 Tomcat安装及环境变量配置
- MAC OSX10.10上搭建Apache,PHP,MySQL5.6.22,phpMyAdmin开发环境
- 穷举法和迭代法
- ASP.NET 5
- Maven中的import scope.
- Eclipse快捷键大全
- Eclipse快捷键大全
- Building COM Objects in C#
- BeanFactory和ApplicationContext实例化Bean 的不同
- Makefile
- 代理模式
- iOS两个关于对象的关键字
- 首次在公司内部的分享
- UVa 1354 Mobile Computing[暴力枚举]
- leetcode:Valid Palindrome 【Java】
- ajax地址三级联动下拉表