首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >基于Vaadin的多线程刷新UI

基于Vaadin的多线程刷新UI
EN

Stack Overflow用户
提问于 2015-07-15 07:13:25
回答 1查看 1.4K关注 0票数 1

我试图在我的Vaadin应用程序中实现多线程刷新UI。

这个UI的一部分是基于容器的dChart。要构建dChart,我将遍历容器并按其属性计算元素,如下所示:

代码语言:javascript
复制
        Collection<?> itemIDS = container.getItemIds();

        for (Object itemID : itemIDS) {
            Property property = container.getContainerProperty(itemID, "status");
            String status = (String) property.getValue();
            if (countMap.containsKey(status)) {
                countMap.put(status, countMap.get(status) + 1);
            } else {
                countMap.put(status, 1);
            }
        }

然而,如果容器有数千个元素,则需要超过2-3秒。

用户只是不能等待这么长时间来刷新UI。

我读到,我可以构建我的UI,然后使用@Push Vaadin注释刷新它,在dChart完全构建之后。

所以我建造了这样的东西:

代码语言:javascript
复制
{
//class with @Push
  void refreshPieDChart(GeneratedPropertyContainer container) {
    new InitializerThread(ui, container).start();
  }

  class InitializerThread extends Thread {
    private LogsUI parentUI;
    private GeneratedPropertyContainer container;

    InitializerThread(LogsUI ui, GeneratedPropertyContainer container) {
        parentUI = ui;
        this.container = container;
    }

    @Override
    public void run() {
        //building dChart etc etc... which takes over 2-3 seconds

        // Init done, update the UI after doing locking
        parentUI.access(new Runnable() {
            @Override
            public void run() {
                chart.setDataSeries(dataSeries).show();
            }
        });
    }
  }
}

但是,如果我刷新页面几次,就会生成有关SQLContainer的错误。

代码语言:javascript
复制
java.lang.IllegalStateException: A trasaction is already active!

因为经过几次刷新之后,我的多个线程使用相同的SQLContainer并行运行。

为了解决这个问题,我想要停止除最后一个之外的所有工作刷新线程,以消除并发问题。我怎么能做到呢?也许还有别的解决办法?

编辑:我尝试过这样做,但问题仍然存在,这是防止并发问题的正确方法吗?

代码语言:javascript
复制
{
  private static final Object mutex = new Object();
//class with @Push
  void refreshPieDChart(GeneratedPropertyContainer container) {
    new InitializerThread(ui, container).start();
  }

  class InitializerThread extends Thread {
    private LogsUI parentUI;
    private GeneratedPropertyContainer container;

    InitializerThread(LogsUI ui, GeneratedPropertyContainer container) {
        parentUI = ui;
        this.container = container;
    }

    @Override
    public void run() {
        //is it correct way to prevent concurrent problem? 
        synchronized(mutex){
            //method to refresh/build chart which takes 2-3s.
        }
        // Init done, update the UI after doing locking
        parentUI.access(new Runnable() {
            @Override
            public void run() {
                chart.setDataSeries(dataSeries).show();
            }
        });
    }
  }
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-07-17 12:54:03

我就是这样做的:

Henri Kerola给我指出了一个非常明显的想法:在SQL中进行计数。正如我所说的,我正在考虑这一点,但这意味着我需要为每一个可能的过滤器组合准备SQL。这是个相当复杂的问题,看起来不太好。

但这使我意识到,如果我使用SQLContainer和过滤器作为我的桌子,我可以做同样的计数。我只需要用我自己的FreeformQueryFreeformStatementDelegate创建第二个FreeformQuery

如果我要创建以上所有,我可以添加相同的过滤器到两个容器,但我不需要计数元素,因为第二个容器为我保存的值。这听起来很复杂,但是看看我的代码:

代码语言:javascript
复制
FreeformQuery myQuery = new MyFreeformQuery(pool);
FreeformQuery countQuery = new CountMyFreeformQuery(pool);

SQLContainer myContainer = new SQLContainer(myQuery);  //here i hold my all records as in a Question
SQLContainer countContainer = new SQLContainer(countQuery);  //here i hold computed count(*) and status

MyFreeformQuery.java看起来像:

代码语言:javascript
复制
class ProcessFreeformQuery extends FreeformQuery {

private static final String QUERY_STRING = "SELECT request_date, status, id_foo FROM foo";
private static final String COUNT_STRING = "SELECT COUNT(*) FROM foo";
private static final String PK_COLUMN_NAME = "id_foo";

MyFreeformQuery(JDBCConnectionPool connectionPool) {
    super(QUERY_STRING, connectionPool, PK_COLUMN_NAME);
    setDelegate(new AbstractFreeformStatementDelegate() {

        protected String getPkColumnName() {
            return PK_COLUMN_NAME;
        }

        protected String getQueryString() {
            return QUERY_STRING;
        }

        protected String getCountString() {
            return COUNT_STRING;
        }
    });
}

最重要的是,CountFreeformQuery.java看起来像:

代码语言:javascript
复制
public class CountFreeformQuery extends FreeformQuery {

private static final String QUERY_STRING_GROUP = "SELECT status, count(*) as num FROM foo GROUP BY status";
private static final String QUERY_STRING = "SELECT status, count(*) as num FROM foo";
private static final String GROUP_BY = "foo.status";

public CountFreeformQuery(JDBCConnectionPool connectionPool) {
    super(QUERY_STRING_GROUP, connectionPool);
    setDelegate(new CountAbstractFreeformStatementDelegate() {
        protected String getQueryString() {
            return QUERY_STRING;
        }

        protected String getGroupBy(){
            return GROUP_BY;
        }

    });
}
}

现在,如果我想像这样在smth之后刷新dChart

代码语言:javascript
复制
myContainer.addContainerFilter(new Between(DATE_PROPERTY, getTimestamp(currentDate), getOneDayAfter(currentDate)));

我对countContainer也是这样

代码语言:javascript
复制
countContainer.addContainerFilter(new Between(DATE_PROPERTY, getTimestamp(currentDate), getOneDayAfter(currentDate)));

并将其传递给不需要计数元素的方法,只需将所有容器添加到映射中,然后像这样添加到dChart:

代码语言:javascript
复制
    Map<String, Long> countMap = new HashMap<String, Long>();
    Collection<?> itemIDS = container.getItemIds();
    for (Object itemID : itemIDS) {
        Property statusProperty = container.getContainerProperty(itemID, "status");
        Property numProperty = container.getContainerProperty(itemID, "num");
        countMap.put((String) statusProperty.getValue(), (Long) numProperty.getValue());
    }

现在,我已经计算了status myContainer中的元素,不需要多线程或写入大量的sql。

谢谢各位的建议。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31423764

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档