具有单个worker和返回值的Java多线程异步调用

时间:2013-01-23 11:43:02

标签: java multithreading

我因为以下棘手的问题而奋战了几天:

我有一个基于tomcat的Web应用程序,可以在任意时间向查询服务器发出请求。可能会同时触发两个或多个请求。

在接收端,我有一个处理这些请求的服务器,在通过spits重新调整结果列表后,在Tomcat上显示结果列表,显示jsp页面上的列表。

服务器只有一个对象的静态实例,实际上是通过数据进行搅拌(创建起来相当昂贵)。

现在,这就是我遇到瓶颈的问题:一方面,来自Tomcat的请求应该同步提供,以便Tomcat内部的管道可以工作并向用户显示合理的结果。 另一方面,如果它们是同步服务的,则处理方法(askQuery())对其他线程显示为锁定,而辅助请求返回为空。

我已经尝试过(几乎)所有可能的事情:在单独的线程中运行处理,同步方法等 - 至今无济于事。如果处理在单独的线程中运行,我无法产生结果并将列表同步返回给Tomcat。如果我加入()线程,该方法将显示为锁定状态。

以下是原始课程,用以说明我想要做的事情:

    package...
    imports...

    public class DLQueryEngineBrain extends ADLQueryEngine{
        protected static Brain brain;
        protected static boolean isFree = true;
        protected AOwlResultParser orp;
        private QueryThread queryThread;


        public DLQueryEngineBrain(String ontologyURL, ThirdPartyBeanManager tpbm) {
            super(ontologyURL, tpbm);
            try {
                DLQueryEngineBrain.brain = new Brain("http://purl.obolibrary.org/obo/", "http://purl.obolibrary.org/obo/fbbt.owl", 32);
                LOG.debug("BRAIN': " + brain + " this " + this);
                brain.learn(ontologyURL);
            } 
            catch (Exception e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
            this.orp = new OwlResultParserClass(this.ontology);
        }

    // This is the method I want to run asynchronously, but I want it to return results //in-line of the calling method, eg. 
    //results = queryEngineBrain.askQuery("sublcass of some X"), and use the results in the //subsequent code...

public synchronized Set<OntBean> askQuery(OntQueryQueue oqq) {
            LOG.debug("Input: " + oqq.toString());
            Set<OntBean> results = new TreeSet<OntBean>();
            QueryThread queryThread = new QueryThread(oqq, (OwlResultParserClass)this.orp);
            queryThread.start();
            while (!queryThread.isFinished){
                try {
                    Thread.sleep(10);
                    LOG.debug("Main Thread sleeps...");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            LOG.debug("Output: " + queryThread.results.size());
            return queryThread.results;
        }

        class QueryThread extends Thread {
            int number;
            private Set<OntBean> results = new TreeSet<OntBean>();
            private OntQueryQueue oqq;
            private OwlResultParserClass orp;
            boolean isFinished = false;


            //Just save the number to display on the console
            public QueryThread(OntQueryQueue oqq, OwlResultParserClass orp) {
                this.number = DLQueryEngineBrain.numThreads++;
                this.oqq = oqq;
                this.orp = orp;
            }

            public void run () {
                while(!this.isFinished) {
                    LOG.debug("DLQueryEngineBrain.isFree: " + DLQueryEngineBrain.isFree);
                    if (DLQueryEngineBrain.isFree){
                        this.runQuery();
                        this.isFinished = true;
                    }
                    else {
                        LOG.debug("Query thread sleeps while brain is busy...");
                        try {
                            Thread.sleep(getRandInt());
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }

            public void runQuery() {
                DLQueryEngineBrain.isFree = false;
                List<String> queries = this.oqq.getQueries();
                LOG.debug("BRAIN : " + DLQueryEngineBrain.brain + " this " + this);
                for (String currExpr: queries){
                    LOG.debug("currExpr: " + currExpr);     
                    List<String> subClasses = null;
                    try {
                        subClasses = DLQueryEngineBrain.brain.getSubClasses(currExpr, true);
                    } catch (ClassExpressionException e) {
                        e.printStackTrace();
                    }
                    LOG.debug("Found: " + subClasses.size());
                    //Iterates over the list and print the result.
                    for (String subClass : subClasses) {
                        this.results.add(orp.getOntBeanForId(subClass));
                    }
                }
                DLQueryEngineBrain.isFree = true;
            }

            private int getRandInt(){
                double d = Math.random();
                int i = Math.round((float)d*100); 
                return i;
            }
        }

    }

问题是askQuery()仍然锁定,所有后续查询都返回空结果。

从askQuery()中删除Thread.sleep()导致它以异步方式工作但是没有返回任何结果 - askQuery()代码只是执行到最后而不等待线程完成。

对于我做错的任何帮助或建议表示赞赏。

2 个答案:

答案 0 :(得分:0)

一个可能的原因是你在queryThread.isFinished上有一个数据竞争:你从主线程读取它并从QueryThread线程写入它而没有同步。

使变量volatile可以解决该问题:volatile boolean isFinished = false;。我不知道它是否能解决你的问题。

作为旁注:

  • 启动线程的想法是并行化作业。在你的情况下,你启动线程,然后等待它完成,因此askQuery没有从使用线程中获得任何好处
  • 一个必然结果是一个活跃问题:你在整个查询工作期间持有一个锁,阻止任何其他线程取得进展

答案 1 :(得分:0)

好的偷看,感谢一百万的帮助。我已经弄明白了。

问题出现在推理器中,但在上游视图控制器中 - 方法:

public synchronized ModelAndView handleRequest(HttpServletRequest req, HttpServletResponse res) throws Exception { .. }`

 public class GeneListController implements Controller{

正在穿越电线并弄乱结果。

解决方案一直瞪着我的眼睛......

Tomcat日志显示每次都正确填充了SQL查询的ID列表(ID列表是所考虑的推理器的产品),但是生成的jsp页面返回为空。

synchronized添加到上述方法后,一切正常。

啊好吧......

第1课:阅读日志

第2课永远不要假设(即使有人告诉你问题是在线程中)