【问题标题】:Synchronizing log entry posting to server同步日志条目发布到服务器
【发布时间】:2011-04-14 09:41:01
【问题描述】:

我的项目有点问题。我正在为 Android 开发一个应用程序,并且想将我的日志发布到服务器。几乎一切正常,但我遇到了一个小问题。我的班级中有一个 ArrayList 来处理帖子:

private ArrayList<String> logsToPost = new ArrayList<String>();

以及将数据放入此列表的方法和一个单独的线程,该线程每 20 秒运行一次,然后上传日志并清空列表。问题是,如果我在线程尝试上传数据并清理列表时尝试写入此列表,则会收到 ConcurrentModificationException。我的代码如下所示:

Iterator<String> keyIter = logsToPost.iterator();
        while (keyIter.hasNext()) {
        String next = keyIter.next();
        String[] params = next.split(FormFactory.FUNCTION_SEP);
        HttpData hd = HttpRequest.post(LOGCAT_LOG_URL, "userId=" + sb.userId + "&pdaId=" + sb.pdaId
                + "&tag=" + params[0] + "&logContent=" + params[1]);
        logsToPost.remove(next);
        }

我知道有一个同步列表和同步块,但我无法让同步列表在我的机器人上工作。你有什么想法我该如何实现这个日志记录程序?我尝试使用 volatile 关键字,但仍然出现异常。

【问题讨论】:

    标签: java concurrency thread-safety synchronized


    【解决方案1】:

    从 Java 的角度来看:

    您的代码应该始终导致异常,因为您使用 ArrayList.remove 而不是 Iterator.remove 修改 ArrayList,同时还对其进行迭代。

    进行此更改应该可以在大多数情况下解决您的问题:

    Iterator<String> keyIter = logsToPost.iterator();
    while (keyIter.hasNext()) {
        String next = keyIter.next();
        //do something
        keyIter.remove();
    }
    

    为了进一步强化您的任务,避免失败的风险,您应该考虑在日志记录任务开始时自动交换 ArrayList 的实例。所以工作流程是:

    ArrayList<String> logsToPost = logListReference.getAndSet(new ArrayList<String>());
    for (String logEntry:logsToPost) {
        //doSomething
    }
    

    【讨论】:

    • 非常感谢,这是我的错误。还有一个问题:让 logsToPost 变得易变有什么不同吗?无论如何我都会添加一个 try / catch 块......
    • 没有。 volatile 基本上解决了线程可见性问题以防止过时的读取。这意味着,如果一个线程修改了一个变量(原始或引用),那么所有其他线程将在下次尝试读取该变量时看到它。嗯...您可能真正在寻找的是 BlockingQueue。
    【解决方案2】:

    在发送过程开始时交换列表是否是一种解决方案。这样发送过程总是使用“旧”列表,而写入过程总是使用“新”列表。

    那么你只需要同步写入新列表,并交换它们,

    让我来说明一下:

    public class ListHolder {
    
      private List newList = new List();
    
      public syncronized List exchangeAndGetOld() {
        List oldList = this.newList;
        this.newList = new List();
        return oldList();
      }
    
      public syncronized addToNewList(Object item) {
        newList.add(item);
      }
    }
    

    【讨论】:

    • 谢谢,这是个好主意。我会将它们结合起来以获得最佳结果。
    【解决方案3】:
    1. 对共享列表使用同步集合:

      私有向量 logsToPost = new Vector();

    2. 在上传数据之前获取该集合的副本:

    ArrayList<String> logsToUpload;
    synchronized(logsToPost) {
      logsToUpload = new ArrayList<String>(logsToPost);
      logsToPost.clear();
    }
    
    1. 在发送循环中使用集合的副本:

      迭代器 keyIter = logsToUpload.iterator();

    【讨论】:

      猜你喜欢
      • 2021-11-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-07-15
      • 1970-01-01
      • 1970-01-01
      • 2019-11-09
      相关资源
      最近更新 更多