mina坑之OrderedThreadPoolExecutor

现象:
客户端消息只能到解码器,到不了IOHandler


MINA启动代码片段如下:
            IoFilter protocol = new ProtocolCodecFilter(codecFactory);
            fcb.addLast("codec", protocol);

            OrderedThreadPoolExecutor threadpoll = new OrderedThreadPoolExecutor(500);
            fcb.addLast("threadPool", new ExecutorFilter(threadpoll));


为了多线程处理,在解码器的下面,我们加了一个线程池,mina自己写的线程池, 这个线程池的作用是对于同一个session来的请求,它能够按照请求到达的时间顺序的执行。举个例子,在一个session中,如果先接收到request A,然后再接收到request B,那么,OrderedThreadPoolExecutor能够保证一定处理完A之后再处理B。而一般的thread pool,会将A和B传递给不同的thread处理,有可能request B会先于request A完成。

为什么消息能过解码器,但是到不了逻辑, 怀疑这个线程池出问题,我简单分析一下这个类的源码:

    private final Set<Worker> workers = new HashSet<Worker>();

    private final AtomicInteger idleWorkers = new AtomicInteger();


workers是一个Worker的集合,每一个Worker都实现了Runnable接口,线程池管理这些Worker线程执行并发的事件处理, idleWorkers则负责及时的统计空闲的Worker数量便于提高性能, 非常形象的命名,workers是一个工厂,里面全部是worker工人,工人负责处理扔过来的Runnable, idleWrokers统计有多少工人空闲, 很显然,idleWorkers的数量必然小于等于workers的数量
private void addWorkerIfNecessary() {
        if (idleWorkers.get() == 0) {
            synchronized (workers) {
                if (workers.isEmpty() || (idleWorkers.get() == 0)) {
                    addWorker();
                }
            }
        }
    }

这个方法是专门给workers添加worker的,但是大家注意,在加之前有个判断,如果idleWrokers大于0肯定就加不进去了

也就是说,如果我们的workers里数量是0了,但是idleWorkers计数却大于0,那这个线程池肯定跪了

问题就出现在下面这段代码:

        public void run() {
            thread = Thread.currentThread();

            try {
                for (;;) {
                    IoSession session = fetchSession();

                    idleWorkers.decrementAndGet();

                    if (session == null) {
                        synchronized (workers) {
                            if (workers.size() > getCorePoolSize()) {
                                // Remove now to prevent duplicate exit.
                                workers.remove(this);
                                break;
                            }
                        }
                    }

                    if (session == EXIT_SIGNAL) {
                        break;
                    }

                    try {
                        if (session != null) {
                            runTasks(getSessionTasksQueue(session));
                        }
                    } finally {
                        idleWorkers.incrementAndGet();
                    }
                }
            } finally {
                synchronized (workers) {
                    workers.remove(this);
                    OrderedThreadPoolExecutor.this.completedTaskCount += completedTaskCount;
                    workers.notifyAll();
                }
            }
        }

上面的是worker的run方法,

这段代码什么情况下会导致 workers里数量是0,但idleWorkers计数却大于0的情况呢

代码看起来似乎不会出现这种情况,因为能出异常的地方,都进行了try处理,

问题恰恰就发生在try里,假如我们的worker里有一个Runnable里出现一个Throwable的异常呢

当代码运行到这行时

                            runTasks(getSessionTasksQueue(session));

这里抛出了一个Throwable的异常,代码里没有处理,所以导致for循环直接退出,到了最后的finally

于是出现了我们不愿意看到的情况,workers数量比idleWorkers少了1,这个异常发生几次,就会导致workers是0,而idleWorkers数量大于0

这时候再有Runnable过来,全部都执行不了了

让我们来看看咱们的JDK的线程池这个地方是怎么做的,会不会出现同样的情况呢

                try {
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    w.completedTasks++;
                    w.unlock();
                }

哈哈哈,看来JDK还是牛逼些,所有错误全部处理了

总结一下,mina这个地方确实是个坑,但是如果框架里try了Throwable也不会出现这个问题

猜你喜欢

转载自watersrc.iteye.com/blog/2345029