博客已经好久都没有写了,感觉自己变慵懒了。。。这本来也是应该早就应该要写的。。。
在前面读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的实现方式来实现一个自己的线程池。。。