简体   繁体   中英

How to declare a volatile list within a concurrent hashmap?

I have a variable:

ConcurrentHashMap<String, List<AnObjectType>> mapOfList;

The List as I understand is not thread safe. But I don't want to use Synchronized keyword as I actually want concurrent read access and synchronized write to the list and not blocking read and writes.

So I would normally declare Volatile on the variable like so:

volatile List<AnObjectType> varName;

(Although in this case I think this referers to Volatile reference on the list, but what I want is for both the list reference and the content of the list to be volatile.)

But how do I do that within a ConcurrentHashMap construct given that I don't declare the list as a variable anywhere but within a method?

ie List is created within method:

if (!mapOfList.containsKey("ListA")) {
     List<AnObjectType> listA=new ArrayList<AnObjectType>();
     mapOfList.put("ListA", listA);
}

and the list is accessed in another method within the same class:

List<AnObjectType> listA=mapOfList.get("ListA");
if (listA!=null) {
     // Do something concurrent with listA.
}

Sub Question: Would something like this work at all?

ConcurrentHashMap<String,List<AtomicReference<AnObjectType>>>>

Elaboration on the list's operations:

The list will be accessed via multiple threads reading almost constantly. Write access to the list will be triggered on certain conditions. So what I want is a concurrent access to the List's content with seldom write operations on the list's content that should be reflected by all reads after the write operation.

Use ConcurrentMap#putIfAbsent for creating the List , and access all lists in a lazy getter:

List<AnObjectType> getList(String key) {
     if (!mapOfList.containsKey(key)) {
     // This list might not end up being the one that gets returned, but
     // that's OK
         mapOfList.putIfAbsent(key, new CopyOnWriteArrayList<AnObjectType>());
     }
     return mapOfList.get(key);
}

The CopyOnWriteArrayList should give you the synchronized performance you need.

You may want to look into java.util.concurrent.locks , particularly ReentrantReadWriteLock . The package Javadocs say

The ReadWriteLock interface similarly defines locks that may be shared among readers but are exclusive to writers. Only a single implementation, ReentrantReadWriteLock, is provided, since it covers most standard usage contexts. But programmers may create their own implementations to cover nonstandard requirements.

The Javadocs for ReentrantReadWriteLock says

ReentrantReadWriteLocks can be used to improve concurrency in some uses of some kinds of Collections. This is typically worthwhile only when the collections are expected to be large, accessed by more reader threads than writer threads, and entail operations with overhead that outweighs synchronization overhead.

and contains an example using a TreeMap .

The general idea is that you create an instance of the ReentrantReadWriteLock per instance of the collection. That then provides you with one Lock instance for read operations, and another for write operations. You then manually manage the locking during access methods.

Below is an untested implementation that should support your use case.

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;


public class RWList<E> extends AbstractList<E> {
    private final List<E> list = new ArrayList<E>();
    private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    private final Lock r = rwl.readLock();
    private final Lock w = rwl.writeLock();

    @Override
    public E set(int index, E element) {
        w.lock();
        try { return list.set(index, element); }
        finally { w.unlock(); }
    }

    @Override
    public void add(int index, E element) {
        w.lock();
        try { list.add(index, element); }
        finally { w.unlock(); }
    }

    @Override
    public E remove(int index) {
        w.lock();
        try { return list.remove(index); }
        finally { w.unlock(); }
    }

    @Override
    public E get(int index) {
        r.lock();
        try { return list.get(index); }
        finally { r.unlock(); }
    }

    @Override
    public int size() {
        r.lock();
        try { return list.size(); }
        finally { r.unlock(); }
    }

}

Vector类是同步的ArrayList,也可以使用Collections.synchronizedList(new ArrayList())

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM