前一篇我看到了创建GraphThreadPoolExecutor多线程池执行器后,会执行run方法,今天继续看如何进行多线程并发执行用例。
//对于同步代码块,就得指定锁对象m_graph
public void run() {
synchronized(m_graph) {
if (DOT_FILES) {
m_dotFiles.add(m_graph.toDot());
}
//从m_graph拿到所有没有依赖其他用例的还没有执行的用例list,然后执行runNodes方法
List<T> freeNodes = m_graph.getFreeNodes();
runNodes(freeNodes);
}
}
该方法主要作用是:从m_graph拿到所有没有依赖其他用例的还没有执行的用例list,然后执行runNodes方法;且该方法是多线程同步运行的;那么runNodes方法又是做了什么呢?来具体再看一下代码
/**
* Create one worker per node and execute them.
*/
private void runNodes(List<T> freeNodes) {
List<IWorker<T>> runnables = m_factory.createWorkers(freeNodes);
for (IWorker<T> r : runnables) {
m_activeRunnables.add(r);
ppp("Added to active runnable");
setStatus(r, Status.RUNNING);
ppp("Executing: " + r);
try {
execute(r);
// if (m_threadCount > 1) execute(r);
// else r.run();
}
catch(Exception ex) {
ex.printStackTrace();
}
}
}
该方法的第一步就是通过m_factory.createWorkers方法新建IWorkerList对象,生成可执行用例工厂;然后就是每个用例的执行过程了,具体可以分为三步:
1.把每一个可执行task加入到activeRunnables中去并输出“Added to active runnable”;
2.设置其状态status为running,并输出“Executing: ”;
3.执行用例。
这三个步骤中最重要的是第三步执行,那么执行又做了哪些工作呢,同样的还是看代码
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
该方法主要是做三个步骤:
1.如果正在运行的线程少于corePoolSize线程,则尝试使用给定命令作为其第一个任务来启动新线程。 对addWorker的调用原子地检查runState和workerCount,从而通过返回false来防止在不应该添加线程的情况下发出错误警报。
2.如果任务可以成功排队,那么我们仍然需要再次检查是否应该添加线程(因为现有线程自上次检查后就死掉了或自进入此方法以来该池已关闭)。 因此,我们重新检查状态,如果已经停止则重新排队,如果在没有线程的情况下则启动新线程。
3.如果我们无法将任务排队,则尝试添加一个新线程。 如果失败,我们知道我们已关闭或已饱和,因此拒绝该任务。