java线程池的实现原理(netty)

博客已经好久都没有写了,感觉自己变慵懒了。。。这本来也是应该早就应该要写的。。。

在前面读netty源代码的时候就可以看到netty本身就自己实现了一个线程池,而且也自己实现了future,并且实现的功能更加的强大。。。future还可以添加listener,这个刚开始自己觉得最为神奇。。当看完了它是怎么实现的之后觉得设计还是挺漂亮的。。。

要自己实现java的线程池,那么有两个接口是需要熟悉的。。。


最为上层的是executor接口,其中就定义了一个方法:

    /**
     * Executes the given command at some time in the future.  The command
     * may execute in a new thread, in a pooled thread, or in the calling
     * thread, at the discretion of the <tt>Executor</tt> implementation.
     *
     * @param command the runnable task
     * @throws RejectedExecutionException if this task cannot be
     * accepted for execution.
     * @throws NullPointerException if command is null
     */
    void execute(Runnable command);
看注释就知道,该方法要实现的功能很简单,就是用来执行提交的runnalbe任务。。

接下来是executorservice接口,它扩展了executor接口,增加了关闭,submit,invokeAll方法。。使得功能更强一些。。

至于说AbstractExecutorService,它是一抽象类,它实现了ExecutorService中的方法,如果我们继承这个类来实现自己的线程池的话,那么需要实现留下的execute方法。。。这里我们就拿其中它的一个方法实现来看看吧:

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
这里调用了newTaskFor方法,将runnable包装为RunnableFuture的类型,也就是java自定义的future类型。。最后在调用execute方法来执行这个task,然后将futuretask按照future返回。。。

好了接下来我们来看看netty线程池的实现吧:

(1)我们可以将executor理解为单个执行任务的处理器,看做单个线程

(2)将executorgroup看成是一个executor的集合,也就是把它看成线程池。。

这里我们就先来看看DefaultEventExecutor的实现吧,先来看看它类型的集成体系:


这次我们从后向前看吧,先来看看DefaultEventExecutor的定义:

final class DefaultEventExecutor extends SingleThreadEventExecutor {

    DefaultEventExecutor(DefaultEventExecutorGroup parent, ThreadFactory threadFactory) {
        super(parent, threadFactory, true);
    }

    //这个方法将会在生成的线程当中被调用,用于不断的来处理已经
    @Override
    protected void run() {
        for (;;) {  //一个循环,不断的将task取出来,然后执行就可以了
            Runnable task = takeTask();
            if (task != null) {
                task.run();
                updateLastExecutionTime();
            }

            if (confirmShutdown()) {
                break;
            }
        }
    }
}
其实它的实现相对还是很简单的,定义了一个run方法,它将会在生成的线程中调用,它完成的功能就是不断的从队列中取出任务然后执行。。。

接下来我们来看看一个比较重要的SingleThreadEventExecutor的定义吧:

首先它定义了两个两个任务队列:

 private final Queue<Runnable> taskQueue;     //当前executor的任务队列
    //这个队列主要是用于处理带有时间延迟的任务,可以将其理解为定时任务
    final Queue<ScheduledFutureTask<?>> delayedTaskQueue = new PriorityQueue<ScheduledFutureTask<?>>();   //带优先权的任务队列
前面的队列默认采用的是LinkedBlockingQueue,这是线程安全的队列,而还有一个优先权队列,这个是用来实现延迟任务的,也就是定时任务。。。可以类比nginx或者libevent的定时的实现。。说白了都是同一个道理,通过时间来作为key,将已经超时的节点选出来。。

这里说一个题外话:java的PriorityQueue是采用堆实现的,小根堆实现的。。有意思吧。。不过这个小根堆的存储炒采用的是数组。。。

我们再来看看在该类型里面定义的线程吧,也就是最终的用于执行任务的线程:

thread = threadFactory.newThread(new Runnable() {    //创建线程
            @Override   //线程的执行函数
            public void run() {
                CURRENT_EVENT_LOOP.set(SingleThreadEventExecutor.this);  //存储当前线程的本地对象
                boolean success = false;
                updateLastExecutionTime();
                try {
                    SingleThreadEventExecutor.this.run();  //开始当前executor的执行函数,run方法延后到了后面的类中实现
                    success = true;
                } catch (Throwable t) {
                    logger.warn("Unexpected exception from an event executor: ", t);
                } finally {
                    if (state < ST_SHUTTING_DOWN) {
                        state = ST_SHUTTING_DOWN;
                    }

                    // Check if confirmShutdown() was called at the end of the loop.
                    if (success && gracefulShutdownStartTime == 0) {
                        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 {
                            synchronized (stateLock) {
                                state = ST_TERMINATED;
                            }
                            threadLock.release();
                            if (!taskQueue.isEmpty()) {
                                logger.warn(
                                        "An event executor terminated with " +
                                        "non-empty task queue (" + taskQueue.size() + ')');
                            }
                        }
                    }
                }
            }
        });
代码比较简单,就不细说了。。

另外在该类型中还定义了一些基本的操作方法,例如takeTask,runAllTask等方法。

最后它还实现了最为重要的execute方法:

    //用于执行一个task,说白了就是把这个task放到任务队列当中去,如果当前executor中定义的线程并没有启动的话,那么要启动它
    @Override
    public void execute(Runnable task) {
        if (task == null) {
            throw new NullPointerException("task");
        }

        boolean inEventLoop = inEventLoop();
        if (inEventLoop) {
            addTask(task);
        } else {
            startThread();
            addTask(task);
            if (isShutdown() && removeTask(task)) {
                reject();
            }
        }

        if (!addTaskWakesUp) {
            wakeup(inEventLoop);
        }
    }
由这部分代码,我们可以看出,对于task的处理都是放入任务队列当中去,然后再在前面提到的run方法中一个一个的处理。。。

接下来我们来看看AbstractEventExecutor类型的一些基本定义吧:

我们就来看看它的submit方法吧:

    //下面无非就是一些提交和调度任务,会调用newTaskFor方法将任务转换为futuretask,这里result为void
    @Override
    public Future<?> submit(Runnable task) {
        return (Future<?>) super.submit(task);
    }
其实是调用的AbstractExecutorService的submit方法,在这里将会将传进来的runnable转化为callable接口,然后再将其转化为futuretask,这里有一点需要注意的是,转化为futuretask的方法被netty重写了,用于生成自己的定义的futuretask,在前面就已经说了netty自己实现了自己的future。。。

我们来看重写的方法吧:

    @Override
    //重写的abstractexecutorservice的方法,用于将提交的任务封装为futuretask,这里封装成promisetask
    protected final <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
        return new PromiseTask<T>(this, runnable, value);
    }
再来看看在 AbstractExecutorServicesubmit方法吧:
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }
这里将会调用已经重载的newTaskFor方法来穿传进来的task转化为netty自己定义的futuretask,也就是promiseTask,然后再调用前面我们提到过的execute方法,它将会把当前的task放入到任务队列当中去,最后再由最终executor的run方法来挨个挨个的处理。。。至此,对于任务的提交和执行的线路就已经比较的清晰了。。(netty自定义的future的部分以后再说吧)

好了。。接下来我们来看看group的实现吧,也就是池子是怎么搞的。。。

这里就选DefaultEventExecutorGroup来分析吧,还是先来看看它的继承体系:


这里我们还是从后向前看吧,来看看DefaultEventExecutorGroup的定义:

public class DefaultEventExecutorGroup extends MultithreadEventExecutorGroup {

    public DefaultEventExecutorGroup(int nThreads) {
        this(nThreads, null);
    }

    public DefaultEventExecutorGroup(int nThreads, ThreadFactory threadFactory) {
        super(nThreads, threadFactory);
    }

    @Override
    protected EventExecutor newChild(
            ThreadFactory threadFactory, Object... args) throws Exception {
        return new DefaultEventExecutor(this, threadFactory);
    }
}
定义比较简单吧,不过这里有定义一个比较重要的方法,newChild,用于生成一个一个的executor,这里的executor也就是在前面说的DefaultEventExecutor。

接下来来看看MultithreadEventExecutorGroup的定义吧:

首先它有两个比较重要的属性:

    private final EventExecutor[] children;
    private final AtomicInteger childIndex = new AtomicInteger();
前面的children为一个数组,用于保存当前这个group所有的executor,而另外一个线程安全的integer会用于简单的负载均衡(真的很简单)

来看看它的构造方法:

    protected MultithreadEventExecutorGroup(int nThreads, ThreadFactory threadFactory, Object... args) {
        if (nThreads <= 0) {
            throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
        }

        if (threadFactory == null) {
            threadFactory = newDefaultThreadFactory();
        }

        children = new SingleThreadEventExecutor[nThreads];
        for (int i = 0; i < nThreads; i ++) {
            boolean success = false;
            try {
            	//调用子类的newChild方法,用于生成一个一个的executor
                children[i] = newChild(threadFactory, args);
                success = true;
            } catch (Exception e) {
                // TODO: Think about if this is a good exception type
                throw new IllegalStateException("failed to create a child event loop", e);
            } finally {
                if (!success) {
                    for (int j = 0; j < i; j ++) {
                        children[j].shutdownGracefully();
                    }

                    for (int j = 0; j < i; j ++) {
                        EventExecutor e = children[j];
                        try {
                            while (!e.isTerminated()) {
                                e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                            }
                        } catch (InterruptedException interrupted) {
                            Thread.currentThread().interrupt();
                            break;
                        }
                    }
                }
            }
        }
    }
其实没有做太多的事情,无非就是创建executor,并将它们保存到数组当中去。。。

另外还有一个比较重要的方法:

    @Override
    public EventExecutor next() {
        return children[Math.abs(childIndex.getAndIncrement() % children.length)];   //获取当前index的值,然后将其+1,比较简单的线程间负载均衡
    }
这里就知道这个所谓的负载均衡有多么简单了吧。。。哈哈。。。

这里我们再来看看AbstractEventExecutorGroup吧,我们就看其中一个方法就可以了:

    @Override    //用于提交任务
    public Future<?> submit(Runnable task) {
        return next().submit(task);
    }
到这里我们就将整个任务的提交过程弄得比较的清晰了。。。

。。。好了。。好像其实这篇文章也没有什么新的内容。。。

但是也知道了,其实要实现一个线程池也不是什么难事,无非是实现一下统一的借口,然后自己在定义一下自己的线程机制就好了。。。完全可以模仿netty的实现方式来实现一个自己的线程池。。。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值