概述
Netty作为一个优秀的异步的事件驱动的框架,实现了一个功能强大的异步回调结构模型。
下面对比看下不同技术对于同一个需求处理的结果。
以烧水泡茶需求为例,烧水和清洗水壶水杯2个线程是可以同时做的。
方式一:Thread.join()
public class ThreadJoin{
public static final int SLEEP_TIME = 500;
static class HotWarterThread extends Thread{
public HotWarterThread(){
super("烧水线程");
}
@Override
public void run() {
System.out.println("洗好水壶");
System.out.println("盛水");
System.out.println("开火");
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("水开了");
}
}
static class WashThread extends Thread{
public WashThread(){
super("清洗线程");
}
@Override
public void run() {
System.out.println("洗茶壶茶杯");
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("洗好了");
System.out.println("装茶叶");
}
}
public static void main(String[] args) throws InterruptedException {
HotWarterThread t1 = new HotWarterThread();
WashThread t2 = new WashThread();
t1.start();
t2.start();
t1.join();
t2.join();
Thread.currentThread().setName("主线程");
System.out.println("泡茶喝");
}
}
洗好水壶
盛水
开火
洗茶壶茶杯
洗好了
装茶叶
水开了
泡茶喝
join()方式有诸多受限,没有返回值,没有办法告诉主线程结果。主线程阻塞。
方式二:Callable接口
Callable接口比Runnable接口更进一步,可以返回线程执行结果。
Future接口里提供了一些方法可以获取任务的执行结果
1、可以通过get()方法获取线程执行结果。如果操作尚未完成,则当前调用线程会阻塞。如果不允许阻塞太长时间或者无限期阻塞,可以通过带超时时间的get方法获取结果。如果到达超时时间仍然没有完成,抛TimeoutException。
2、通过isDone方法判断当前的异步操作是否完成。如果完成,无论成功与否,返回true,否则返回false。
3、通过cancel方法尝试取消异步操作,结果未知。如果操作已经发生,或者其他原因无法取消,取消失败。
Callable接口的call()方法可以获得线程执行结果。
FutureTask继承了Callable、Runnable接口,作为要执行的任务传给线程,承接着桥梁作用。
public class FutureTaskTest {
public static final int SLEEP_TIME = 500;
static class HotWarterThread implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
System.out.println("洗好水壶");
System.out.println("盛水");
System.out.println("开火");
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
return false;
}
System.out.println("水开了");
return true;
}
}
static class WashThread implements Callable<Boolean> {
@Override
public Boolean call() throws Exception {
System.out.println("洗茶壶茶杯");
try {
Thread.sleep(SLEEP_TIME);
} catch (InterruptedException e) {
return false;
}
System.out.println("洗好了");
System.out.println("装茶叶");
return true;
}
}
public static void drinkTea(boolean b1,boolean b2){
if(b1 && b2){
System.out.println("开始泡茶喝");
}else if(!b1){
System.out.println("烧水失败");
}else if(!b2){
System.out.println("清洗失败");
}
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
Callable<Boolean> job1 = new HotWarterThread();
Callable<Boolean> job2 = new WashThread();
FutureTask<Boolean> f1 = new FutureTask<>(job1);
FutureTask<Boolean> f2 = new FutureTask<>(job2);
Thread t1 = new Thread(f1,"烧水线程");
Thread t2 = new Thread(f2,"清洗线程");
t1.start();
t2.start();
boolean b1 = f1.get();
boolean b2 = f1.get();
drinkTea(b1,b2);
}
}
洗好水壶
盛水
开火
洗茶壶茶杯
洗好了
装茶叶
水开了
开始泡茶喝
使用JDK5提供的Future接口实现需求也有不足之处:虽然能够获取其他线程的执行结果了,Future.get()方法是阻塞的,不是异步的。
方法三:
Netty自己基于JDK的Future实现了自己的一些异步回调技术。主要的接口有Future(Netty)和GenericFutureListener,用于表示异步执行完成的监听器。
在Netty中,绝大部分的操作都是异步的。例如客户端连接服务端:
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
.childOption(ChannelOption.SO_KEEPALIVE, true)
.option(ChannelOption.SO_BACKLOG, 128)
.childHandler(new ChatServerInitializer());
ChannelFuture channelFuture = serverBootstrap.bind(40000).addListener(new ChannelFutureListener() {
@Override
public void operationComplete(ChannelFuture future) throws Exception {
if (future.isSuccess()) {
System.out.println("成功");
} else {
System.out.println("失败");
future.cause().printStackTrace();
}
}
});
如果服务端启动占用端口失败,会打印失败和堆栈信息:
Netty提供的Future接口和JDK的Future同名,但是提供了诸多方法,例如
通过 isDone 方法来判断当前操作是否完成;
通过 isSuccess 方法来判断已完成的当前操作是否成功;
通过 getCause 方法来获取已完成的当前操作失败的原因;
通过 isCancelled 方法来判断已完成的当前操作是否被取消;
通过 addListener 方法来注册监听器,当操作已完成(isDone 方法返回完成),将会通知指定的监听器;如果 Future 对象已完成,则通知指定的监听器