代码之家  ›  专栏  ›  技术社区  ›  Bart van Heukelom

高效的缓存同步

  •  3
  • Bart van Heukelom  · 技术社区  · 15 年前

    考虑一下这个

    public Object doGet() {
        return getResource();
    }
    
    private Object getResource() {
        synchronized (lock) {
            if (cachedResourceIsStale()) {
                downloadNewVersionOfResource();
            }
        }
    
        return resource;
    }
    

    假设 doGet 将被同时执行,并且很多时候,下载新版本的资源需要一段时间,是否有更有效的方法来执行同步 getResource ?我知道读/写锁,但我不认为它们可以在这里应用。

    为什么要同步?如果缓存过时,在第一个线程刷新资源时访问该资源的所有线程都将执行自己的刷新。除此之外,这种方法也很难有效。

    正如Balusc在评论中提到的,我目前在servlet中面对这个问题,但是我对一般的答案很满意,因为谁知道在什么情况下我会再次遇到它。

    2 回复  |  直到 15 年前
        1
  •  4
  •   meriton    15 年前

    1. doGet()
    2. cachedPageIsStale()
    3. downloadNewVersionOfResource()

    synchronized (lock) {
        c++;
    }
    

        2
  •  1
  •   Victor Sorokin    15 年前


    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)];
        }
    }