多线程:连续写入共享数据结构并定期清除

时间:2018-10-22 14:55:19

标签: java multithreading

我有一个问题,我有多个线程在不断写入,例如并发HashMap。现在,我想定期(通过TimerJob)处理该哈希图中的所有内容。其他线程仍可以继续对其进行写操作(该新数据将在下次Timejob启动时进行处理)。

我想知道什么是实现这一目标的最佳方法。我正在阅读,这个问题似乎很像Triple Buffer。我对此并不十分乐观。

有什么想法吗?

编辑:我想在处理完地图后将其从地图中删除,以免最终重新处理该数据

编辑:我不必一定要将数据写入HashMap / Set。我只需要将其放入一个集合中,即可在其他线程仍在对其进行写入的同时定期处理该集合。

2 个答案:

答案 0 :(得分:1)

我不确定您是否需要地图中的所有数据,或者您不需要在地图中由计时器作业处理的数据。

如果您只需要计时器工作之类的快照,则可以像这样用新地图来切换/替换地图。

private volatile ConcurentHashMap map ;

public void processByTimerJob(){

   ConcurentHashMap oldMap = this.map;
   this.map = new ConcurrentHashMap; // everyting new will be stored in new map    
   oldMap.forEach(.....   //process old map via iteration or whatever you want

}

答案 1 :(得分:0)

我会使用double buffering和一个读/写锁。

双重缓冲通过允许处理换出的图来减少保留。

使用读/写锁定使我可以确定在交换之后,仍然没有人在向地图写入数据。

class DoubleBufferedMap<K, V> extends AbstractMap<K, V> implements Map<K, V> {
    // Used whenever I want to create a new map.
    private final Supplier<Map<K, V>> mapSupplier;
    // The underlying map.
    private volatile Map<K, V> map;
    // My lock - for ensuring no-one is writing.
    ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    final Lock readLock = readWriteLock.readLock();
    final Lock writeLock = readWriteLock.writeLock();

    public DoubleBufferedMap(Supplier<Map<K, V>> mapSupplier) {
        this.mapSupplier = mapSupplier;
        this.map = mapSupplier.get();
    }

    /**
     * Swaps out the current map with a new one.
     * 
     * @return the old map ready for processing, guaranteed to have no pending writes.
     */
    public Map<K,V> swap() {
        // Grab the existing map.
        Map<K,V> oldMap = map;
        // Replace it.
        map = mapSupplier.get();
        // Take a write lock to wait for all `put`s to complete.
        try {
            writeLock.lock();
        } finally {
            writeLock.unlock();
        }
        return oldMap;
    }

    // Put methods must take a read lock (yeah I know it's weird)

    @Nullable
    @Override
    public V put(K key, V value) {
        try{
            // Take a read-lock so they know when I'm finished.
            readLock.lock();
            return map.put(key, value);
        } finally {
            readLock.unlock();
        }
    }

    @Override
    public void putAll(@NotNull Map<? extends K, ? extends V> m) {
        try{
            // Take a read-lock so they know when I'm finished.
            readLock.lock();
            map.putAll(m);
        } finally {
            readLock.unlock();
        }
    }

    @Nullable
    @Override
    public V putIfAbsent(K key, V value) {
        try{
            // Take a read-lock so they know when I'm finished.
            readLock.lock();
            return map.putIfAbsent(key, value);
        } finally {
            readLock.unlock();
        }
    }


    // All other methods are simply delegated - but you may wish to disallow some of them (like `entrySet` which would expose the map to modification without locks).

    @Override
    public Set<Entry<K, V>> entrySet() {
        return map.entrySet();
    }

    @Override
    public boolean equals(Object o) {
        return map.equals(o);
    }

    @Override
    public int hashCode() {
        return map.hashCode();
    }

    // ... The rest of the delegators (left to the student)