BrightLoong’s Blog

Create the cache object entityCache.java

public class EntityCache {
    /** * The data saved */
    private  Object datas;
    
    /** * Sets the data expiration time. A value of 0 indicates that the data will never expire. */
    private  long timeOut;
    
    /** * last refresh time */
    private  long lastRefeshTime;
    
    public EntityCache(Object datas, long timeOut, long lastRefeshTime) {
        this.datas = datas;
        this.timeOut = timeOut;
        this.lastRefeshTime = lastRefeshTime;
    }
    public Object getDatas(a) {
        return datas;
    }
    public void setDatas(Object datas) {
        this.datas = datas;
    }
    public long getTimeOut(a) {
        return timeOut;
    }
    public void setTimeOut(long timeOut) {
        this.timeOut = timeOut;
    }
    public long getLastRefeshTime(a) {
        return lastRefeshTime;
    }
    public void setLastRefeshTime(long lastRefeshTime) {
        this.lastRefeshTime = lastRefeshTime; }}Copy the code

Define the cache operation interface, icachEmanager.java

public interface ICacheManager {
    /** * store in cache *@param key
     * @param cache
     */
    void putCache(String key, EntityCache cache);
    
    /** * store in cache *@param key
     * @param cache
     */
    void putCache(String key, Object datas, long timeOut);
    
    /** * get the corresponding cache *@param key
     * @return* /
    EntityCache getCacheByKey(String key);
    
    /** * get the corresponding cache *@param key
     * @return* /
    Object getCacheDataByKey(String key);
    
    /** * get all caches *@param key
     * @return* /
    Map<String, EntityCache> getCacheAll(a);
    
    /** * check whether the cache is in *@param key
     * @return* /
    boolean isContains(String key);
    
    /**
     * 清除所有缓存
     */
    void clearAll(a);
    
    /** * Clear the corresponding cache *@param key
     */
    void clearByKey(String key);
    
    /** * Whether the cache expires due to timeout *@param key
     * @return* /
    boolean isTimeOut(String key);
    
    /** * get all keys *@return* /
    Set<String> getAllKeys(a);
}
Copy the code

Implementation interface ICacheManager, CacheManagerImpl. Java

Here I used ConcurrentHashMap to store the cache, which I thought was thread-safe, but it’s not thread-safe, as I’ll see later in the test.

public class CacheManagerImpl implements ICacheManager {
    private static Map<String, EntityCache> caches = new ConcurrentHashMap<String, EntityCache>();

    /** * store in cache *@param key
     * @param cache
     */
    public void putCache(String key, EntityCache cache) {
        caches.put(key, cache);
    }
    
    /** * store in cache *@param key
     * @param cache
     */
    public void putCache(String key, Object datas, long timeOut) {
        timeOut = timeOut > 0 ? timeOut : 0L;
        putCache(key, new EntityCache(datas, timeOut, System.currentTimeMillis()));
    }
    
    /** * get the corresponding cache *@param key
     * @return* /
    public EntityCache getCacheByKey(String key) {
        if (this.isContains(key)) {
            return caches.get(key);
        }
        return null;
    }
    
    /** * get the corresponding cache *@param key
     * @return* /
    public Object getCacheDataByKey(String key) {
        if (this.isContains(key)) {
            return caches.get(key).getDatas();
        }
        return null;
    }

    /** * get all caches *@param key
     * @return* /
    public Map<String, EntityCache> getCacheAll(a) {
        return caches;
    }
    
    /** * check whether the cache is in *@param key
     * @return* /
    public boolean isContains(String key) {
        return caches.containsKey(key);
    }

    /**
     * 清除所有缓存
     */
    public void clearAll(a) {
        caches.clear();
    }
    
    /** * Clear the corresponding cache *@param key
     */
    public void clearByKey(String key) {
        if (this.isContains(key)) { caches.remove(key); }}/** * Whether the cache expires due to timeout *@param key
     * @return* /
    public boolean isTimeOut(String key) {
        if(! caches.containsKey(key)) {return true;
        }
        EntityCache cache = caches.get(key);
        long timeOut = cache.getTimeOut();
        long lastRefreshTime = cache.getLastRefeshTime();
        if (timeOut == 0 || System.currentTimeMillis() - lastRefreshTime >= timeOut) {
            return true;
        }
        return false;
    }

    /** * get all keys *@return* /
    public Set<String>  getAllKeys(a) {
        returncaches.keySet(); }}Copy the code

Cachelistener.java, which listens for invalid data and removes it.

public class CacheListener{
    Logger logger = Logger.getLogger("cacheLog");
    private CacheManagerImpl cacheManagerImpl;
    public CacheListener(CacheManagerImpl cacheManagerImpl) {
        this.cacheManagerImpl = cacheManagerImpl;
    }
    
    public void startListen(a) {
        new Thread(){
            public void run(a) {
                while (true) {
                    for(String key : cacheManagerImpl.getAllKeys()) {
                        if (cacheManagerImpl.isTimeOut(key)) {
                         cacheManagerImpl.clearByKey(key);
                         logger.info(key + "Cache cleared"); } } } } }.start(); }}Copy the code

The test class TestCache. Java

public class TestCache {
    Logger logger = Logger.getLogger("cacheLog");
    /** * Test cache and cache invalidation */
    @Test
    public void testCacheManager(a) {
        CacheManagerImpl cacheManagerImpl = new CacheManagerImpl();
        cacheManagerImpl.putCache("test"."test".10 * 1000L);
        cacheManagerImpl.putCache("myTest"."myTest".15 * 1000L);
        CacheListener cacheListener = new CacheListener(cacheManagerImpl);
        cacheListener.startListen();
        logger.info("test:" + cacheManagerImpl.getCacheByKey("test").getDatas());
        logger.info("myTest:" + cacheManagerImpl.getCacheByKey("myTest").getDatas());
        try {
            TimeUnit.SECONDS.sleep(20);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        logger.info("test:" + cacheManagerImpl.getCacheByKey("test"));
        logger.info("myTest:" + cacheManagerImpl.getCacheByKey("myTest"));
    }
    
    /** * test thread safety */
    @Test
    public void testThredSafe(a) {
        final String key = "thread";
        final CacheManagerImpl cacheManagerImpl = new CacheManagerImpl();
        ExecutorService exec = Executors.newCachedThreadPool();
        for (int i = 0; i < 100; i++) {
            exec.execute(new Runnable() {
                public void run(a) {
                        if(! cacheManagerImpl.isContains(key)) { cacheManagerImpl.putCache(key,1.0);
                        } else {
                            Since the +1 and assignment operations are not atomic, they are wrapped in a Synchronize block
                            synchronized (cacheManagerImpl) {
                               int value = (Integer) cacheManagerImpl.getCacheDataByKey(key) + 1; 
                               cacheManagerImpl.putCache(key,value , 0); }}}}); } exec.shutdown();try {
            exec.awaitTermination(1, TimeUnit.DAYS);
        } catch(InterruptedException e1) { e1.printStackTrace(); } logger.info(cacheManagerImpl.getCacheDataByKey(key).toString()); }}Copy the code

TestCacheManager () displays the following output:

2017-4-17 10:33:51 io.github.brightloong.cache.TestCache testCacheManager information:test:test
2017-4-17 10:33:51 io.github.brightloong.cache.TestCache testCacheManager information: myTest: myTest 10:34:01 IO 2017-4-17 dead simple. Brightloong. Cache. CacheListenerThe $1The run information:testThe cache is cleared the 2017-4-17 10:34:06 IO. Making. Brightloong. Cache. CacheListenerThe $1The run information: myTest cache cleared 2017-4-17 10:34:11 IO. Making. Brightloong. Cache. TestCachetestCacheManager information:test:null
2017-4-17 10:34:11 io.github.brightloong.cache.TestCache testCacheManager information: myTest:nullCopy the code

TestThredSafe () outputs the following result (one of the various results is selected for example) :

2017-4-17 10:35:36 io.github.brightloong.cache.TestCache testThredSafe message: 96Copy the code

You can see it’s not exactly what you want 100. Why is that? ConcurrentHashMap can only guarantee atomicity of a single operation, but cannot guarantee atomicity of a compound operation when used in combination.

if(! cacheManagerImpl.isContains(key)) { cacheManagerImpl.putCache(key,1.0);
                        }
Copy the code

When multithreading, the value is repeatedly updated to 1, so the result is not 100 as expected. The solution is to add synchronized to CacheManagerImpl. Java, but this is a serial operation, and using ConcurrentHashMap doesn’t make sense, but a simple cache is ok. Or add a synchronized block to run in the test method. I can’t think of a more efficient way, I hope you can give me more advice.