Ehcache BlockingCache 源码分析

BlockingCache是对Ehcache进行的扩展,BlockingCache内置了读写锁,不需要用户显示调用。

要彻底分析BlockingCache的原理,需要首先来看一下它内部用到的一些类。

//锁的管理器接口

public interface CacheLockProvider {

    /**

     * 根据key获取锁

     * 这个实现需要保证给定相同的key必须返回同一把锁

     */

    Sync getSyncForKey(Object key);

}
public interface StripedReadWriteLock extends CacheLockProvider {



    /**

     * 根据key获得Java的ReadWriteLock

     */

    ReadWriteLock getLockForKey(Object key);



    /**

     * 获取所有锁

     */

    List<ReadWriteLockSync> getAllSyncs();



}

上面的俩个接口是用来管理锁的,待会我们再看具体实现,再来看一下锁的接口。

public interface Sync {

    /**

     * Acquire lock of LockType.READ or WRITE

     * @param type the lock type to acquire

     */

    void lock(LockType type);



    /**

     * Tries to acquire a LockType.READ or WRITE for a certain period

     * @param type the lock type to acquire

     * @param msec timeout

     * @return true if the lock got acquired, false otherwise

     * @throws InterruptedException Should the thread be interrupted

     */

    boolean tryLock(LockType type, long msec) throws InterruptedException;



    /**

     * Releases the lock held by the current Thread.

     * In case of a LockType.WRITE, should the lock not be held by the current Thread, nothing happens 

     * @param type the lock type to acquire

     */

    void unlock(LockType type);

    

    /**

     * Returns true is this is lock is held at given level by the current thread.

     * 

     * @param type the lock type to test

     * @return true if the lock is held

     */

    boolean isHeldByCurrentThread(LockType type);

}
/**

 * 这个类是Sync接口的实现类,底层用到了Java原生的ReentrantReadWriteLock

 * ReentrantReadWriteLock是读写锁,不了解的可以先去学习一下这个类的使用方法

 * 不然会很难理解,其实ReadWriteLockSync的类的方法都是调用ReentrantReadWriteLock。

 */

public class ReadWriteLockSync implements Sync {



    //Java的读写锁,其实这个类底层都是调用它.

    private final ReentrantReadWriteLock rrwl;



    public ReadWriteLockSync() {

        this(new ReentrantReadWriteLock());

    }



    public ReadWriteLockSync(ReentrantReadWriteLock lock) {

        this.rrwl = lock;

    }

    /**

     * 根据类型获取锁.

     */

    public void lock(final LockType type) {

        getLock(type).lock();

    }



    /**

     * 在给定时间内尝试获取锁.

     */

    public boolean tryLock(final LockType type, final long msec) throws InterruptedException {

        return getLock(type).tryLock(msec, TimeUnit.MILLISECONDS);

    }



    /**

     * 释放锁.

     */

    public void unlock(final LockType type) {

        getLock(type).unlock();

    }



    //根据枚举类型获取写入锁OR读取锁.

    private Lock getLock(final LockType type) {

        switch (type) {

            case READ:

                return rrwl.readLock();

            case WRITE:

                return rrwl.writeLock();

            default:

                throw new IllegalArgumentException("We don't support any other lock type than READ or WRITE!");

        }

    }



    public ReadWriteLock getReadWriteLock() {

      return rrwl;

    }

    

    /**

     * 判断当前线程是否获取了写入锁.

     */

    public boolean isHeldByCurrentThread(LockType type) {

        switch (type) {

            case READ:

                throw new UnsupportedOperationException("Querying of read lock is not supported.");

            case WRITE:

                return rrwl.isWriteLockedByCurrentThread();

            default:

                throw new IllegalArgumentException("We don't support any other lock type than READ or WRITE!");

        }

    }

}

 

StripedReadWriteLockSync类是负责管理锁的,会根据传入的key的hash值计算出数组的下标,原理与hashmap一样。

/**

 * 这个类是锁管理器的实现类,用来管理锁.

 * 这个类的实现原理就是内部维护一个锁的数组(默认2048个)

 * 然后根据传入的key通过hash算法获取到一个int类型的值,这个值就是锁数组的下标.

 * 这样就可以针对不同的key分别锁定提高并发效率

 */

public class StripedReadWriteLockSync implements StripedReadWriteLock {



    /**

     * 默认锁的数量必须是2的幂

     */

    public static final int DEFAULT_NUMBER_OF_MUTEXES = 2048;



    //锁的数组

    private final ReadWriteLockSync[] mutexes;

    //锁的list

    private final List<ReadWriteLockSync> mutexesAsList;



    public StripedReadWriteLockSync() {

        this(DEFAULT_NUMBER_OF_MUTEXES);

    }



    /**

     * 构造函数,初始化锁.

     */

    public StripedReadWriteLockSync(int numberOfStripes) {

        if ((numberOfStripes & (numberOfStripes - 1)) != 0) {

            throw new CacheException("Cannot create a CacheLockProvider with a non power-of-two number of stripes");

        }

        if (numberOfStripes == 0) {

            throw new CacheException("A zero size CacheLockProvider does not have useful semantics.");

        }



        //初始化数组.

        mutexes = new ReadWriteLockSync[numberOfStripes];



        //初始化ReadWriteLockSync锁放入数组.

        for (int i = 0; i < mutexes.length; i++) {

            mutexes[i] = new ReadWriteLockSync();

        }

        mutexesAsList = Collections.unmodifiableList(Arrays.asList(mutexes));

    }



    /**

     * 根据key获取锁,相同的key获取到同一把锁。

     */

    public ReadWriteLockSync getSyncForKey(final Object key) {

        //根据key的hash算法在与数组的长度计算出数组下标,这个算法会保证得到的int值在0-数组长度之内不会越界.

        int lockNumber = ConcurrencyUtil.selectLock(key, mutexes.length);

        return mutexes[lockNumber];

    }



    /**

     * 根据key获取锁

     */

    public ReadWriteLock getLockForKey(final Object key) {

        int lockNumber = ConcurrencyUtil.selectLock(key, mutexes.length);

        return mutexes[lockNumber].getReadWriteLock();

    }



    /**

     * Returns all internal syncs

     * @return all internal syncs

     */

    public List<ReadWriteLockSync> getAllSyncs() {

        return mutexesAsList;

    }

}

 

BlockingCache核心代码分析,在使用它的时候有一点需要注意,如果一个线程get方法获取element,当获取不到时会返回null,这时线程并没有释放写入锁,这点一定要注意。

所以必须要调用blockingCache.put(new Element(key, null)) 来释放锁。

/**

 * A blocking decorator for an Ehcache, backed by a {@link Ehcache}.

 */

public class BlockingCache extends EhcacheDecoratorAdapter {



    /**

     * The amount of time to block a thread before a LockTimeoutException is thrown

     */

    protected volatile int timeoutMillis;



    private final int stripes;



    //线程安全的引用AtomicReference

    private final AtomicReference<CacheLockProvider> cacheLockProviderReference;



    private final OperationObserver<GetOutcome> getObserver = operation(GetOutcome.class).named("get").of(this).tag("blocking-cache").build();



    /**

     * Creates a BlockingCache which decorates the supplied cache.

     */

    public BlockingCache(final Ehcache cache, int numberOfStripes) throws CacheException {

        super(cache);

        this.stripes = numberOfStripes;

        this.cacheLockProviderReference = new AtomicReference<CacheLockProvider>();

    }



    /**

     * Creates a BlockingCache which decorates the supplied cache.

     */

    public BlockingCache(final Ehcache cache) throws CacheException {

        this(cache, StripedReadWriteLockSync.DEFAULT_NUMBER_OF_MUTEXES);

    }



    private CacheLockProvider getCacheLockProvider() {

        CacheLockProvider provider = cacheLockProviderReference.get();

        while (provider == null) {

            cacheLockProviderReference.compareAndSet(null, createCacheLockProvider());

            provider = cacheLockProviderReference.get();

        }

        return provider;

    }



    //初始化StripedReadWriteLockSync

    private CacheLockProvider createCacheLockProvider() {

        Object context = underlyingCache.getInternalContext();

        if (underlyingCache.getCacheConfiguration().isTerracottaClustered() && context != null) {

            return (CacheLockProvider) context;

        } else {

            return new StripedReadWriteLockSync(stripes);

        }

    }



    /**

     * Retrieve the EHCache backing cache

     */

    protected Ehcache getCache() {

        return underlyingCache;

    }



    /**

     * 获取元素

     */

    @Override

    public Element get(final Object key) throws RuntimeException, LockTimeoutException {

        getObserver.begin();



        //通过key获取锁,不同的key在大多数情况获取的锁都是不同的,所以性能会很好.

        Sync lock = getLockForKey(key);



        //获取读取锁,读取锁是可以并发读的,所以效率会很好.

        acquiredLockForKey(key, lock, LockType.READ);



        Element element;

        try {

            //在真正的cache里获取元素

            element = underlyingCache.get(key);

        } finally {

            //释放读取锁

            lock.unlock(LockType.READ);

        }



        //如果元素为空,则获取写入锁,写入锁不可以并发进入的。

        if (element == null) {

            acquiredLockForKey(key, lock, LockType.WRITE);



            //再次获取元素,如果为空则直接返回,注意当前线程并没有释放锁.这里一定要注意。

            element = underlyingCache.get(key);

            if (element != null) {

                //如果元素不为空,说明已经有线程在其它时刻put进去元素了,那么直接释放锁就OK了。

                lock.unlock(LockType.WRITE);

                getObserver.end(GetOutcome.HIT);

            } else {

                getObserver.end(GetOutcome.MISS_AND_LOCKED);

            }

            return element;

        } else {

            getObserver.end(GetOutcome.HIT);

            return element;

        }

    }



   

    private void acquiredLockForKey(final Object key, final Sync lock, final LockType lockType) {

        if (timeoutMillis > 0) {

            try {

                 //如果设置了超时时间,那么在给定时间内获取不到锁就抛异常.

                boolean acquired = lock.tryLock(lockType, timeoutMillis);

                if (!acquired) {

                    StringBuilder message = new StringBuilder("Lock timeout. Waited more than ")

                            .append(timeoutMillis)

                            .append("ms to acquire lock for key ")

                            .append(key).append(" on blocking cache ").append(underlyingCache.getName());

                    throw new LockTimeoutException(message.toString());

                }

            } catch (InterruptedException e) {

                throw new LockTimeoutException("Got interrupted while trying to acquire lock for key " + key, e);

            }

        } else {

            //获取锁,获取不到就会一直等待知道获取到锁。

            lock.lock(lockType);

        }

    }



    protected Sync getLockForKey(final Object key) {

        return getCacheLockProvider().getSyncForKey(key);

    }



    /**

     * put元素自动释放锁,主要看doAndReleaseWriteLock.

     */

    @Override

    public void put(final Element element) {



        doAndReleaseWriteLock(new PutAction<Void>(element) {

            @Override

            public Void put() {

                if (element.getObjectValue() != null) {

                    underlyingCache.put(element);

                } else {

                    underlyingCache.remove(element.getObjectKey());

                }

                return null;

            }

        });

    }





    private <V> V doAndReleaseWriteLock(PutAction<V> putAction) {



        if (putAction.element == null) {

            return null;

        }



        Object key = putAction.element.getObjectKey();



        //根据key获取锁.

        Sync lock = getLockForKey(key);



        //判断一下当前线程是否已经获取了写入锁,如果已经获取到锁,那么说明当前线程是执行get方法是元素为null时获取到锁的.

        //否则说明是另一个新的线程直接调用put的方法,所以需要重新获取锁.

        if (!lock.isHeldByCurrentThread(LockType.WRITE)) {

            lock.lock(LockType.WRITE);

        }

        try {

            return putAction.put();

        } finally {

            //在这里释放锁.

            lock.unlock(LockType.WRITE);

        }

    }







    private abstract static class PutAction<V> {



        private final Element element;



        private PutAction(Element element) {

            this.element = element;

        }

        abstract V put();

    }

}

 

你可能感兴趣的:(BlockingCache)