java - 多个线程尝试以毫秒为键进行写入,但在 ConcurrentHashMap 中创建的不是一个键,而是许多键

标签 java concurrency concurrenthashmap

ConcurrentHashMap<Long, CopyOnWriteArrayList<Observation>
mostRecentObservationDB = new ConcurrentHashMap<Long,
    CopyOnWriteArrayList<Observation>(524288, 0.75f, 32);

这是我的 map 。我试图同时使用多个线程进行读写,但不知何故它总是创建多个键。

long timeInMilliseconds = System.currentTimeMillis();

if (/* the last key older than 10 seconds comparing to the new key*/) {
    CopyOnWriteArrayList<Observation> initializingObservation = new CopyOnWriteArrayList<>();
    initializingObservation.add(obs);

    mostRecentObservationDB.putIfAbsent(timeInMilliseconds, initializingObservation);
} else {
  // Update
}

单独线程,通过删除超过 10 秒的键来过滤此 HashMap 。

while (true) {
    try {
        Thread.sleep(4000);
        if(/*Time (key) older than 10 seconds*/) {
            mostRecentObservationDB.remove(key);
        }

    } catch (Exception e) {

    }
}

问题在于,删除 key 后,它会在初始化时创建多个 key 。这是我的日志。

key -> 1501779153776, value
key -> 1501779153826, value
key -> 1501779153876, value
key -> 1501779153896, value

我希望在删除操作时将它们存储为一键。这就是它应该被存储的方式。

key -> 1501779153776, value

但是,当我从中读取内容然后通过 remove() 方法删除所有条目时,我希望在读取 map 内容的过程中没有其他线程写入 map 然后删除它们。

<小时/>

这是行为奇怪的代码:

public static void main(String[] args) {
    ConcurrentHashMap<Long, String> tenSecondBucket =
        new ConcurrentHashMap<Long, String>();

    Thread writingThread = new Thread(new Runnable() {
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(1);

                    if(tenSecondBucket.size() > 0) {
                        // getting last key
                        long lastKey = 0;
                        for (long keyValue : tenSecondBucket.keySet()) {
                            lastKey = keyValue;
                        }

                        if(System.currentTimeMillis() - lastKey > 10000) {
                            tenSecondBucket.put(System.currentTimeMillis(), "secondEntry");
                        } else {
                            tenSecondBucket.put(lastKey, "updatedEntry");
                        }
                    } else {
                        tenSecondBucket.put(System.currentTimeMillis(), "newEntry");
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    writingThread.start();

    Thread removingThread = new Thread(new Runnable() {
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(4000);

                    if(tenSecondBucket.size() > 0) {
                        tenSecondBucket.keySet().stream().forEach(key -> {
                            if(System.currentTimeMillis() - key > 10000) {
                                tenSecondBucket.remove(key);
                            }
                        });
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    removingThread.start();

    Thread readingThread = new Thread(new Runnable() {
        @Override
        public void run() {
            while (true) {
                try {
                    Thread.sleep(4000);

                    if(tenSecondBucket.size() > 0) {
                        tenSecondBucket.keySet().stream().forEach(key -> {
                            System.out.println("testing key which is timestamp " + key);
                        });
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });

    readingThread.start();
}

最佳答案

问题在于您派生“lastKey”的方式。您似乎需要 map 中存在的最高时间值,并且您假设它将是 tenSecondBucket.keySet() 的最后一个条目。但是,keySet() 方法返回一个 Set,它本质上是不排序的(在任何情况下,映射都不维护键的有序列表)

所以你需要替换这段代码-

long lastKey = 0;
for (long keyValue : tenSecondBucket.keySet()) {
     lastKey = keyValue;
}

使用此代码 -

long lastKey = 0;
for (long keyValue : tenSecondBucket.keySet()) {
    lastKey = keyValue > lastKey? keyValue : lastKey;
}

更改后,代码可以正常工作
请注意,代码仍然有改进/重构的空间

关于java - 多个线程尝试以毫秒为键进行写入,但在 ConcurrentHashMap 中创建的不是一个键,而是许多键,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45490005/

相关文章:

java - 更改变量但保留它保存?

java - 使用基于 Java 的客户端实现的最简单的应用程序服务器是什么?

java - 应用程序使用 Intent.ACTION_SEND 时处理手机之间的差异

java - 为什么ConcurrentHashMap中的HashEntry是final的?

java - 为什么 ConcurrentHashMap 不能为每个桶都加锁?

java - 如何为选定行着色 TableViewer Java

java - 在Web应用程序中实现生产者-消费者的最佳方式

concurrency - 如何使用 ZooKeeper 实现 Chubby 风格的锁序列器?

java - Docx4j 库不是线程安全的。解决此问题的可能方法有哪些?

java - 了解代码特定的功能概念