用最实用的NioEventLoop来看,其实,一个事件循环是一个内建selector的线程罢了,eventLoop是事件循环的意思,说明这个线程一直在执行,而且一直在循环的执行,知道外部给下下命令,让它停止。
基本的事件循环写法
for(;;){
//做一些事情
}
最基本的写法。在循环里面,要处理网络读写,还有用户任务的处理,这里为什么要处理用户任务也放到事件循环里面来做呢?其实,大部分的用户任务依然是网络写,所以,在这里面合情合理。
NioEventLoop是当第一个任务投递进来,才进行真正的创建线程操作,在其父类SingleThreadEventExecutor当中的execute方法中可见:
@Override public void execute(Runnable task) { if (task == null) { throw new NullPointerException("task"); } boolean inEventLoop = inEventLoop(); addTask(task); if (!inEventLoop) { startThread(); if (isShutdown()) { boolean reject = false; try { if (removeTask(task)) { reject = true; } } catch (UnsupportedOperationException e) { // The task queue does not support removal so the best thing we can do is to just move on and // hope we will be able to pick-up the task before its completely terminated. // In worst case we will log on termination. } if (reject) { reject(); } } } if (!addTaskWakesUp && wakesUpForTask(task)) { wakeup(inEventLoop); } }
这里的startThread方法就是真正启动线程的方法,如下所示:
private void startThread() { if (state == ST_NOT_STARTED) { if (STATE_UPDATER.compareAndSet(this, ST_NOT_STARTED, ST_STARTED)) { try { doStartThread(); } catch (Throwable cause) { STATE_UPDATER.set(this, ST_NOT_STARTED); PlatformDependent.throwException(cause); } } } }
可以发现,首先判断是否已经启动线程,若没有启动,则调用doStartThread方法,如下所示:
private void doStartThread() { assert thread == null; executor.execute(new Runnable() { @Override public void run() { thread = Thread.currentThread(); if (interrupted) { thread.interrupt(); } boolean success = false; updateLastExecutionTime(); try { SingleThreadEventExecutor.this.run(); success = true; } catch (Throwable t) { logger.warn("Unexpected exception from an event executor: ", t); } finally { for (;;) { int oldState = state; if (oldState >= ST_SHUTTING_DOWN || STATE_UPDATER.compareAndSet( SingleThreadEventExecutor.this, oldState, ST_SHUTTING_DOWN)) { break; } } // Check if confirmShutdown() was called at the end of the loop. if (success && gracefulShutdownStartTime == 0) { if (logger.isErrorEnabled()) { logger.error("Buggy " + EventExecutor.class.getSimpleName() + " implementation; " + SingleThreadEventExecutor.class.getSimpleName() + ".confirmShutdown() must " + "be called before run() implementation terminates."); } } try { // Run all remaining tasks and shutdown hooks. for (;;) { if (confirmShutdown()) { break; } } } finally { try { cleanup(); } finally { // Lets remove all FastThreadLocals for the Thread as we are about to terminate and notify // the future. The user may block on the future and once it unblocks the JVM may terminate // and start unloading classes. // See https://github.com/netty/netty/issues/6596. FastThreadLocal.removeAll(); STATE_UPDATER.set(SingleThreadEventExecutor.this, ST_TERMINATED); threadLock.release(); if (!taskQueue.isEmpty()) { if (logger.isWarnEnabled()) { logger.warn("An event executor terminated with " + "non-empty task queue (" + taskQueue.size() + ')'); } } terminationFuture.setSuccess(null); } } } } }); }
这里先判断,调用Executor的execute方法投递任务,这里是调用外部类的run方法,这里thread = Thread.currentThread()对thread成员变量进行了赋值。
这个NioEventLoop的写法确实跟我以前的写法不一样。在以前,如果我想让一个对象具有线程特性,成为一个主动对象,那么我会让这个类实现 Runnable接口,然后在一个方法内部创建线程,如下所示:
this.thread = new Thread(this);
this.thread.start();
这样的形式,单这里netty却不是,它使用了自己定义的io.netty.util.concurrent.ThreadPerTaskExecutor 类执行创建这个线程的工作,其实,NioEventLoop也是一个Executor,所以,你也能猜到了,这里用到了装饰器模式啦。
继承关系如下所示:
io.netty.util.concurrent.EventExecutorGroup
io.netty.util.concurrent.EventExecutor
io.netty.util.concurrent.AbstractEventExecutor
io.netty.util.concurrent.AbstractScheduledEventExecutor
io.netty.util.concurrent.SingleThreadEventExecutor
io.netty.channel.SingleThreadEventLoop
io.netty.channel.nio.NioEventLoop
可以看出,实现思路几个方面:
1.eventLoop可以提交任务,虽然eventLoop是单线程的,但是,我们要把它当做线程池来用。
2.eventLoop必须能够处理io任务
3.具有能够执行周期调度任务的能力,(延迟任务,固定周期执行任务),这样能够跟网络的异步操作特性相匹配,编程比较友好。
io.netty.util.concurrent.EventExecutor就是能够执行任务的接口类,是我们能够接触的最核心的类
稍后再完善。