Search code examples
javathread-synchronization

How to efficiently synchronize rarely changed data?


I have a class member that is frequently read from one thread but rarely updated from another thread. How do I efficiently synchronize access to it? I imagine that if I just synchronize every read and write most read synchronizations will be wasted since the object doesn't change between them.

Including code example although it's pretty simple:

public class A {

    public static class B {
    }

    private List<B> bs =
        new ArrayList<>();

    public void addB(B b) {
        synchronized (this) {
            bs.add(b);
        }
    }

    public void run() {
        while (true) {
            List<B> bs;
            synchronized (this) {
                bs = new ArrayList<>(this.bs);
            }
            bs.forEach(b -> { /* do something */ });
        }
    }
}

assuming addB and run run from different threads.


Solution

  • ReadWriteLock

    You can use ReadWriteLock:

    ReadWriteLock lock=new ReentrantReadWriteLock();
    
    //Read access:
    try{
        lock.readLock().lock();
        //Read access here
    }finally{
        lock.readLock().unlock()
    }
    
    //Write access
    try{
        lock.writeLock().lock();
        //Write access here
    }finally{
        lock.writeLock().unlock()
    

    This should minimize the overhead on readers as nothing is blocked when reading only.

    synchronized

    Another possibility is intrinsic synchronisation:

    synchronized(LOCK){
        //Read or write access here
    }
    

    If most of the work is done in a single thread and there is just a single reader thread (like in your scenario), this would be the most perfornant solution.

    Use thread-safe classes

    A third option could be to use thread safe data classes/data structures. This would not require you to do any synchronization by yourself. In your case, CopyOnWriteArrayList would be a good option. It is optimized for many read and rare write accesses.

    public class A {
    
        public static class B {
        }
    
        private List<B> bs =
            new CopyOnWriteArrayList<>();//important
    
        public void addB(B b) {
            //No synchronized needed
            bs.add(b);
        }
    
        public void run() {
            while (true) {
                //No synchronized block needed, just iterate over bs as it is thread safe
                bs.forEach(b -> { /* do something */ });
            }
        }
    }
    

    Whatever you choose, you should measure the performance impact if you care about it.

    For measuring performance, you can use tools like JMH.