Efficient cache synchronization

Consider this

public Object doGet() {
    return getResource();
}

private Object getResource() {
    synchronized (lock) {
        if (cachedResourceIsStale()) {
            downloadNewVersionOfResource();
        }
    }

    return resource;
}

Assuming that it doGetwill run simultaneously, and much of it, and that downloading a new version of a resource takes some time, are there more efficient ways to perform synchronization in getResource? I know about read / write locks, but I don't think they can be applied here.

Why synchronize at all? If the cache becomes obsolete, all threads accessing the resource, when it is still updated first, will perform their own update. Among other problems, this leads to inefficiency.

As BalusC notes in the comments, I am currently facing this problem in the servlet, but I am happy with the general answers, because who knows what situation I will encounter with it again.

+3
2

  • , doGet()
  • cachedPageIsStale()
  • downloadNewVersionOfResource()

, , . , - , , , , downloadNewVersionOfResource() ( - ).

, . ( -) - , doGet(); , . (, , , .)

PS

. , , NewVersionOfResource(), .

Edit

? , 100 , , , , , . 100 * 10 ^ -6/1 = 0,0001 . - . 10 ^ -8 . , , . , . , , , . , , . :

synchronized (lock) {
    c++;
}

50 (5 * 10 ^ -8 ), 10 hm. 20 , , , , . , , -, , , 1 , 50 , 0,005%. , : -)

, . , nano .

+4

, , ( , ), "" - , , .
, , . - . , , . StripedLock:

import java.util.concurrent.locks.ReentrantLock;

/**
 * Striped locks holder, contains array of {@link java.util.concurrent.locks.ReentrantLock}, on which lock/unlock
 * operations are performed. Purpose of this is to decrease lock contention.
 * <p>When client requests lock, it gives an integer argument, from which target lock is derived as follows:
 * index of lock in array equals to <code>id & (locks.length - 1)</code>.
 * Since <code>locks.length</code> is the power of 2, <code>locks.length - 1</code> is string of '1' bits,
 * and this means that all lower bits of argument are taken into account.
 * <p>Number of locks it can hold is bounded: it can be from set {2, 4, 8, 16, 32, 64}.
  */
public class StripedLock {
    private final ReentrantLock[] locks;

    /**
     * Default ctor, creates 16 locks
     */
    public StripedLock() {
        this(4);
    }

    /**
     * Creates array of locks, size of array may be any from set {2, 4, 8, 16, 32, 64} 
     * @param storagePower size of array will be equal to <code>Math.pow(2, storagePower)</code>
     */
    public StripedLock(int storagePower) {
        if (storagePower < 1 || storagePower > 6)
             throw new IllegalArgumentException("storage power must be in [1..6]");
        int lockSize = (int) Math.pow(2, storagePower);
        locks = new ReentrantLock[lockSize];
        for (int i = 0; i < locks.length; i++)
            locks[i] = new ReentrantLock();
    }

    /**
     * Locks lock associated with given id.
     * @param id value, from which lock is derived
     */
    public void lock(int id) {
        getLock(id).lock();
    }

    /**
     * Unlocks lock associated with given id.
     * @param id value, from which lock is derived 
     */
    public void unlock(int id) {
        getLock(id).unlock();
    }

    /**
     * Map function between integer and lock from locks array
     * @param id argument
     * @return lock which is result of function 
     */
    private ReentrantLock getLock(int id) {
        return locks[id & (locks.length - 1)];
    }
}
+1

Source: https://habr.com/ru/post/1771355/


All Articles