📜 ⬆️ ⬇️

We write a cache with a certain storage time of objects using java.util.concurrent

Not so long ago, I had the task to write a cache that cleans itself after some certain time. The requirements for it were as follows:
  1. Lightness
  2. Thread safety

In general, that's all. Before writing this task with java.util.concurrent, I did not have it. The idea of ​​using this package came from a colleague of mine who had something similar, but did not correspond to the functionality that was needed. So, let's begin:

The key will be the inner class, which, in addition to the direct assignment, will determine whether it is “alive” or can be removed from the cache, since its lifetime has come to an end:

private static class Key { private final Object key; private final long timelife; public Key(Object key, long timeout) { this.key = key; this.timelife = System.currentTimeMillis() + timeout; } public Key(Object key) { this.key = key; } public Object getKey() { return key; } public boolean isLive(long currentTimeMillis) { return currentTimeMillis < timelife; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Key other = (Key) obj; if (this.key != other.key && (this.key == null || !this.key.equals(other.key))) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 43 * hash + (this.key != null ? this.key.hashCode() : 0); return hash; } @Override public String toString() { return "Key{" + "key=" + key + '}'; } } 


The cache class itself is parameterized. Inside we need a storage container. java.util.concurrent.ConcurrentHashMap is best suited. The default storage time is sold in a separate field. Next, create a java.util.concurrent.ScheduledExecutorService:
')
 public class CacheUtil<K, V> { private ConcurrentHashMap<Key, V> globalMap = new ConcurrentHashMap<Key, V>(); private long default_timeout; private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread th = new Thread(r); th.setDaemon(true); return th; } }); } 

We will make the thread a demon, so that at the end of the main thread, the process that cleans the cache also ends.

In the constructor, we will launch our sheduler, which after a certain time (in our case it is one fifth of the storage time of the object.) Will run over the map and delete all those objects whose lifetime has expired:

  public CacheUtil(long default_timeout) throws Exception { if (default_timeout < 100) { throw new Exception("Too short interval for storage in the cache. Interval should be more than 10 ms"); } default_timeout = default_timeout; scheduler.scheduleAtFixedRate(new Runnable() { @Override public void run() { long current = System.currentTimeMillis(); for (Key k : globalMap.keySet()) { if (!k.isLive(current)) { globalMap.remove(k); } } } }, 1, default_timeout/5, TimeUnit.MILLISECONDS); } 


Next, add methods to work with the cache - and everything is ready for use. Here is the full code:

 public class Cahe<K, V> { private volatile ConcurrentHashMap<Key, V> globalMap = new ConcurrentHashMap<Key, V>(); private long default_timeout; private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread th = new Thread(r); th.setDaemon(true); return th; } }); /** * @param default_timeout   -       . */ public Cache(long default_timeout) throws Exception { if (default_timeout < 10) { throw new Exception("Too short interval for storage in the cache. Interval should be more than 10 ms"); } this.default_timeout = default_timeout; scheduler.scheduleAtFixedRate(new Runnable() { @Override public void run() { long current = System.currentTimeMillis(); for (Key k : globalMap.keySet()) { if (!k.isLive(current)) { globalMap.remove(k); } } } }, 1, default_timeout/5, TimeUnit.MILLISECONDS); } /** * @param default_timeout   -        */ public void setDefault_timeout(long default_timeout) throws Exception { if (default_timeout < 100) { throw new Exception("Too short interval for storage in the cache. Interval should be more than 10 ms"); } this.default_timeout = default_timeout; } /** *       *      * @param <K> * @param <V> * @param key    * @param data  */ public void put(K key, V data) { globalMap.put(new Key(key, default_timeout), data); } /** *       * @param <K> * @param <V> * @param key    * @param data  * @param timeout        */ public void put(K key, V data, long timeout) { globalMap.put(new Key(key, timeout), data); } /** *     * @param <K> * @param <V> * @param key      * @return      */ public V get(K key) { return globalMap.get(new Key(key)); } /** *        * @param <K> * @param key -  */ public void remove(K key) { globalMap.remove(new Key(key)); } /** *      */ public void removeAll() { globalMap.clear(); } /** *     . *    . * @param <K> * @param <V> * @param map    */ public void setAll(Map<K, V> map) { ConcurrentHashMap tempmap = new ConcurrentHashMap<Key, V>(); for (Entry<K, V> entry : map.entrySet()) { tempmap.put(new Key(entry.getKey(), default_timeout), entry.getValue()); } globalMap = tempmap; } /** *       *    . * @param <K> * @param <V> * @param map    */ public void addAll(Map<K, V> map) { for (Entry<K, V> entry : map.entrySet()) { put(entry.getKey(), entry.getValue()); } } private static class Key { private final Object key; private final long timelife; public Key(Object key, long timeout) { this.key = key; this.timelife = System.currentTimeMillis() + timeout; } public Key(Object key) { this.key = key; } public Object getKey() { return key; } public boolean isLive(long currentTimeMillis) { return currentTimeMillis < timelife; } @Override public boolean equals(Object obj) { if (obj == null) { return false; } if (getClass() != obj.getClass()) { return false; } final Key other = (Key) obj; if (this.key != other.key && (this.key == null || !this.key.equals(other.key))) { return false; } return true; } @Override public int hashCode() { int hash = 7; hash = 43 * hash + (this.key != null ? this.key.hashCode() : 0); return hash; } @Override public String toString() { return "Key{" + "key=" + key + '}'; } } } 

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


All Articles