-
2018-07-13 15:19:15
Cache是基于python的线程缓存插件,类似于redis的key,value形式的线程化数据存储功能,使用方便,相对于不是很复杂的缓存任务,Cache完全可以满足需求
使用方法:
Cache().setex(k, v, timeout) #设置缓存及过期时间 Cache().get(k) #获取缓存
项目地址: github.com
更多相关内容 -
SemQueue_多线程_缓存队列_源码
2021-10-01 00:09:01适用于windows平台和linux平台的缓存队列实现,支持多线程。 -
多线程共享缓存中冗余路访问消除机制研究
2021-04-04 02:09:26多线程共享缓存中冗余路访问消除机制研究 -
LRU(多线程)缓存的实现
2018-09-20 14:27:28多线程实现缓存的思路有很多种,有基于ttl淘汰策略的,有基于lru淘汰实现的,而在淘汰的方式也有很多选择,如果选用Concurrenthashmap,可以很好的基于ttl淘汰策略,具体实现是另开一个守护线程,定时淘汰...前面写了个lru缓存,适用于单线程的场景。多线程实现缓存的思路有很多种,有基于ttl淘汰策略的,有基于lru淘汰实现的,而在淘汰的方式也有很多选择,如果选用Concurrenthashmap,可以很好的基于ttl淘汰策略,具体实现是另开一个守护线程,定时淘汰ConcurrenthashMap中的过期键,而在使用lru淘汰策略时则需要额外借助辅助结构双向链表,需要额外的同步操作,由于在本文之前实现了一个lru缓存算法https://blog.csdn.net/qq_32459653/article/details/82766468,故放弃使用现有的Concurrenthashmap结构
模仿jdk1.7Concurrenthashmap结构的实现,实现自己的一个同步lru缓存,实现如下
package Inter.other; /** * 缓存通用实现接口接口 * Created by lin on 2018/9/19. */ public interface Cache<K, V> { <V> V get(K key); void set(K key, V value); void clear(); int size(); /** * 该方法,专门为segmentCache设计, * * @return */ void removeLast(); }
package Inter.other; import java.util.HashMap; import java.util.Map; /** * 缓存算法的具体实现 * Created by lin on 2018/9/16. * 时间复杂度为O(1)的一个缓存 */ public class LRUCache<K, V> implements Cache<K, V> { // private KeyGenerationStrategy<K, V> keyGenerationStrategy; //默认容量大小 private static final int DEFAULT_CAPACITY = 8; /* 缓存容量的大小 */ private volatile int capacity; /* 缓存已使用的容量 */ private volatile int size; /* 为了实现快速寻找,这里使用map,查找时间复杂度为O(1)*/ private volatile Map<K, Node<K, V>> map = new HashMap<K, Node<K, V>>(); /* 为了实现快速替换,这里使用链表,删除或者加入时间复杂度为O(1)*/ private volatile Node<K, V> head; private volatile Node<K, V> tail; /** * 初始化 * * @param capacity */ public LRUCache(int capacity) { // map = new HashMap<>(); if (capacity <= 0) { capacity = DEFAULT_CAPACITY; } this.capacity = capacity; this.head = new Node<K, V>(null, null, null); this.tail = new Node<K, V>(head, null, null); head.next = tail; } public LRUCache() { this(DEFAULT_CAPACITY); } /** * 从缓存中获取指定值,没有返回空 * * @param * @param <V> * @return */ public <V> V get(K key) { Node<K, V> node = (Node<K, V>) map.get(key); if (node == null) { return null; } else { moveToFirst(node); return node.value; } } /** * 指定节点添加到缓存中 * * @param key value值对应的键 * @param value 存放的值 */ public void set(K key, V value) { Node<K, V> node = new Node(value, key); //缓存容量未满,不需要淘汰,直接添加到最后一个 if (size <= capacity) { node.prev = head; node.next = head.next; head.next.prev = node; head.next = node; map.put(node.key, node); size++; } else {//容量已满,淘汰最后一个节点即可 // map.put((K)node.key, node); Node delNode = tail.prev; delNode.prev.next = node; node.prev = delNode.prev; node.next = tail; tail.prev = node; delNode.next = null; delNode.prev = null; delNode = null; map.remove(delNode.key); } } //清空缓存 public void clear() { this.head = new Node<K, V>(null, null, null); this.tail = new Node<K, V>(head, null, null); head.next = tail; size = 0; } public int size() { return this.size; } public void removeLast() { if (size() == 0) { return; } Node delNode = tail.prev; Node node = delNode.prev; tail.prev = node; node.next = tail; delNode.next = null; delNode.prev = null; delNode = null; map.remove(delNode.key); } /** * 当节点被访问时需要放置到缓存最前面 * * @param node */ private void moveToFirst(Node node) { //validationIsSwap(); if (node == head.next) { return; } Node<K, V> nodePrev = node.prev; Node<K, V> nodeNext = node.next; Node beMoved = head.next;// 头节点的下一个节点 head.next = node; node.prev = head; node.next = beMoved; beMoved.prev = node; nodePrev.next = nodeNext; nodeNext.prev = nodePrev; } /** * 确定是否可以交换,如果size小于等于1 则没必要 * <p> * private void validationIsSwap() { * if (size <= 1) { * throw new IllegalArgumentException("缓存容量不大于1,不能进行该操作"); * } * } */ public static void main(String[] args) { LRUCache<String, Integer> lruCache = new LRUCache(20); KeyGenerationStrategy<String, Integer> keyGenerationStrategy = new SimpleKeyGenerationStrategy<String, Integer>(); String key1 = keyGenerationStrategy.generationKey(1); String key2 = keyGenerationStrategy.generationKey(2); String key3 = keyGenerationStrategy.generationKey(3); lruCache.set(key1, 1); lruCache.set(key2, 2); lruCache.set(key3, 3); System.out.println(lruCache.get(key1) + ""); ; System.out.println(lruCache.get(key2) + ""); ; System.out.println(lruCache.get(key3) + ""); ; System.out.println(lruCache.get(key1) + ""); ; // lruCache.swapAndFirst(node2); Node head = lruCache.head; //第一个 head = head.next; System.out.println(head); //第二个 head = head.next; System.out.println(head); //第三个 head = head.next; System.out.println(head); // lruCache.set(node1); } private Node getHead() { return this.head; } }
package Inter.other; /** * 链表节点的定义 * Created by lin on 2018/9/16. */ public class Node<K,V> { final V value; final K key;//表示该节点的键; volatile Node next; volatile Node prev; public Node(V value, K key) { this.value = value; this.key = key; } public Node(Node prev, Node next, V value) { this.prev = prev; this.next = next; this.value = value; } public K getKey() { return this.key; } @Override public String toString() { return "prev:" + prev.value + "当前节点" + this.value + "next:" + next.value; } }
package Inter.other; /** * 键值生成策略接口 * Created by lin on 2018/9/19. */ public interface KeyGenerationStrategy<K, V> { K generationKey(V value); }
package Inter.other; /** * 简单的键值生成 * Created by lin on 2018/9/19. */ public class SimpleKeyGenerationStrategy<K, V> implements KeyGenerationStrategy<K, V> { public K generationKey(V value) { return (K) value.toString(); } }
到这里为止 与单线程的lru实现都差不多,只是将一些字段变为用volatile,或final修饰了,并增加了removelast方法;
接下来就模仿jdk1.7ConcurrentHashmap实现并发量高,线程安全的缓存,
package Inter.other; import lombok.Getter; import lombok.Setter; import java.io.Serializable; import java.util.Random; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * Created by lin on 2018/9/20. */ public abstract class SegmentCache<K, V> implements Cache<K, V> { protected CacheFactory cacheFactory; protected final Random random; //主要用于淘汰时,随机选用一个桶淘汰 /*桶的数量 ,其大小可以任意指定,不一定非要2的整数幂*/ @Getter private final int segmentCount; @Getter protected volatile AtomicInteger size; //缓存容量大小,可以改变,但不建议那么做 @Setter protected volatile int capacity; /** * 采用分段锁锁的思路,这里每一个cache都有一个 * ReadWriteLock ,在操作cache时,需要获取对应的ReadWriteLock */ protected final Segment<K, V>[] caches; private static final int DEFAULT_SEGMENTCOUNT = Runtime.getRuntime().availableProcessors(); /** * 初始化一个segmentCache缓存,考虑到缓存可能分布不均匀,故给 * 每个segment分配的容量大小均是capacity的大小,实际容量由SegmentCache * 控制,给每个segment的容量设置为capacity并不会浪费内存,因为并没有实际分配 * 内存空间,仅仅是一个阈值 * * @param segmentCount 分段的数量 * @param capacity 容量大小 * @param cache 默认的缓存实现 */ public SegmentCache(int segmentCount, int capacity, Cache<K, V> cache) { if (capacity <= 0) { throw new IllegalArgumentException("capacity 必须大于0"); } if (segmentCount <= 0) { throw new IllegalArgumentException("segmentCount 必须大于0"); } this.segmentCount = segmentCount; caches = new Segment[segmentCount]; this.capacity = capacity; setCacheFactory();//设置缓存工厂 for (int i = 0; i < segmentCount; i++) { cache = cacheFactory.getCache(cache.getClass().getSimpleName(), capacity); caches[i] = new Segment<K, V>(cache, capacity); } random = new Random(segmentCount); } public SegmentCache(int capacity, Cache<K, V> cache) { this(DEFAULT_SEGMENTCOUNT, capacity, cache); } public <V> V get(K key) { int place = getSegmentPlace(key); Segment<K, V> cache = (Segment<K, V>) caches[place]; return cache.get(key); } public void set(K key, V value) { int place = getSegmentPlace(key); Segment<K, V> cache = caches[place]; while (size.get() < capacity) {//小于 int nowSize = size.get(); if (size.compareAndSet(nowSize, nowSize + 1)) { //先扩容,在添加 cache.set(key, value); break; } continue; } weekout(); //递归调用自身重新设置 set(key, value); } /** * 淘汰键值, */ public abstract void weekout(); public void clear() { } /** * 返回缓存中已存在的键值得大小 * * @return */ public int size() { return size.get(); } static final class Segment<K, V> extends ReentrantReadWriteLock implements Serializable, Cache<K, V> { transient volatile int size; // segment中元素的的数量 transient volatile int capacity; // 缓存容量的大小 transient int modCount; //对的大小造成影响的操作的数量(比如put或者remove操作) private volatile Cache<K, V> cache; //segment的缓存结构 public Segment(Cache<K, V> cache, int capacity) { this.cache = cache; this.capacity = capacity; } public <V> V get(K key) { readLock().lock(); try { return cache.get(key); } finally { readLock().unlock(); } } public void set(K key, V value) { writeLock().lock(); try { cache.set(key, value); } finally { writeLock().unlock(); } } public void clear() { writeLock().lock(); try { cache.clear(); } finally { writeLock().unlock(); } } public int size() { return cache.size(); } public void removeLast() { writeLock().lock(); try { cache.removeLast(); } finally { writeLock().unlock(); } } } /** * 考虑到事实情况,segmentCount可以任意指定大小, * * @param key * @return */ private int getSegmentPlace(K key) { return key.hashCode() % segmentCount; } public abstract void setCacheFactory(); }
缓存工厂,用于决定,使用哪种基本的缓存策略
package Inter.other; /** * Created by lin on 2018/9/20. */ public interface CacheFactory<K, V> { Cache<K, V> getCache(String name, int capacity); }
package Inter.other; /** * Created by lin on 2018/9/20. */ public class SimpleCacheFactory implements CacheFactory { public Cache getCache(String name, int capacity) { if (name.equalsIgnoreCase("lruCache")) { return new LRUCache(capacity); } throw new IllegalArgumentException("没有该种缓存"); } }
package Inter.other; /** * Created by lin on 2018/9/20. */ public class SimpleSegmentCache<K, V> extends SegmentCache<K, V> { public SimpleSegmentCache(int capacity, Cache<K, V> cache) { super(capacity, cache); } public void removeLast() { weekout(); } public void weekout() { while (true) { if (size.get() < capacity) { break; } int weedSegmentPlace = random.nextInt(); Segment weedSegment = caches[weedSegmentPlace]; if (weedSegment.size() > 0) { weedSegment.writeLock().lock(); try { if (size.get() < capacity) { return; } if (weedSegment.size() > 0) { weedSegment.removeLast(); size.decrementAndGet(); break; } } finally { weedSegment.writeLock().unlock(); } } } } public void setCacheFactory() { this.cacheFactory = new SimpleCacheFactory(); } }
另外对lru算法非常感兴趣的同学可以看一些大牛写的线程安全的高并发 lru缓存算法,具体地址如下https://blog.csdn.net/njchenyi/article/details/8046914
-
煤矿安全监控系统多线程双数据缓存数据处理技术-论文
2021-07-08 11:32:31设计了带有多线程安全的双数据缓存技术,传感器数据帧进入队列存放后,监控分站对数据帧逐一进行解析处理后,封装成以太网数据包发送给中心站软件,另外考虑避免饥饿或死锁发生,设计的数据缓存技术加入了多线程安全保护... -
多线程中的可见性问题
2020-08-16 21:34:381.缓存导致的可见性问题 可见性问题是指一个线程修改了某一个共享变量的值时,其他线程是否能够立即知道这个修改。 对于串行程序来说,可见性问题是不存在的,因为你在任何一个操作步骤中修改了某个变量,在后续的...1.缓存导致的可见性问题
可见性问题是指一个线程修改了某一个共享变量的值时,其他线程是否能够立即知道这个修改。
- 对于串行程序来说,可见性问题是不存在的,因为你在任何一个操作步骤中修改了某个变量,在后续的步骤中读取这个变量的值时,读取的一定是修改后的新值。
- 在并行程序中,如果一个线程修改了某一个全局变量,那么其他线程未必可以马上知道这个改动。多核时代,每颗 CPU 都有自己的缓存,这时 CPU 缓存与内存的数据一致性就没那么容易解决了,一个CPU缓存中的变量对另外一个CPU是不可见的
如上图中,两个线程在执行时,CPU1和CPU2分别将内存中的变量,缓存到CPU的cache或者寄存器中,如果CPU1的线程修改了变量V,那么CPU2的线程可能无法意识到这个改动,依然会读取CPU2中cache或者寄存器中的值,这就产生了可见性问题。
2.解决办法
1.使用volatile关键字
volatile 是禁用CPU缓存的意思,变量volatile int x = 0
,它表达的是:告诉编译器,对这个变量的读写,不能使用 CPU 缓存,必须从内存中读取或者写入。2.使用
synchronized
加锁
JMM(java内存模型)关于synchronized的两条规定:- 线程解锁前(退出synchronized代码块之前),必须把共享变量的最新值刷新到主内存中,也就是说线程退出synchronized代码块值后,主内存中保存的共享变量的值已经是最新的了
- 线程加锁时(进入synchronized代码块之后),将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意:加锁与解锁需要是同一把锁)
- 两者结合:线程解锁前对共享变量的修改在下次加锁时对其他线程可见
3.
synchronized
和volatile
的比较1.
volatile
不需要加锁,比synchronized
更轻量级,不会阻塞线程
2.从内存可见性角度讲,volatile
读操作=进入synchronized
代码块(加锁),volatile
写操作=退出synchronized
代码块(解锁)
3.synchronized
既能保证可见性,又能保证原子性,而volatile
只能保证可见性,不能保证原子性 -
基于分区缓存区重放与多线程交互的多智能体深度强化学习算法.pdf
2021-08-18 00:30:57基于分区缓存区重放与多线程交互的多智能体深度强化学习算法.pdf -
多线程并发条件下创建一个缓存
2018-05-25 22:05:13* @desc 写一个多线程情况下缓存小例子 */ public class CacheLock { private Map,Object> cacheMap = new ConcurrentHashMap, Object>(); /** * 根据key获取value * @param key * @return */ private ...import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; /** * @author yzhang * @date 2018/5/25 21:43 * @desc 写一个多线程情况下缓存小例子 */ public class CacheLock { private Map<String,Object> cacheMap = new ConcurrentHashMap<String, Object>(); /** * 根据key获取value * @param key * @return */ private ReadWriteLock lock = new ReentrantReadWriteLock(); public Object getCache(String key){ //1.先获取读锁 lock.readLock().lock(); Object result = cacheMap.get(key); try{ //2.判断是否存在数据 if(result==null){ //3.如果数据不存在,那么就去数据库获取数据,此时应该先释放读锁,并开启写锁 lock.readLock().unlock(); //4.为了防止所线程,这里还需要再次判断是否存在数据 if(cacheMap.get(key)==null){ try { lock.writeLock().lock(); //5.从数据库里面获取数据 //........... }finally { //6.释放写锁,这里是为了防止,当前线程操作异常,所以必须要释放写锁 lock.writeLock().unlock(); } } //7.再一次开启读锁 lock.readLock().lock(); } }finally { //8.防止程序失败,强制释放读锁 lock.readLock().unlock(); } //9.返回结果 return result; } }
-
下载网络图片 (整合多线程、内存缓存、本地文件缓存~).zip项目安卓应用源码下载
2022-03-09 10:12:54下载网络图片 (整合多线程、内存缓存、本地文件缓存~).zip项目安卓应用源码下载下载网络图片 (整合多线程、内存缓存、本地文件缓存~).zip项目安卓应用源码下载 1.适合学生毕业设计研究参考 2.适合个人学习研究参考... -
Android应用源码之下载网络图片 (整合多线程、内存缓存、本地文件缓存~).zip项目安卓应用源码下载
2022-03-08 20:51:13Android应用源码之下载网络图片 (整合多线程、内存缓存、本地文件缓存~).zip项目安卓应用源码下载Android应用源码之下载网络图片 (整合多线程、内存缓存、本地文件缓存~).zip项目安卓应用源码下载 1.适合学生毕业... -
c#串口通信类,实现多线程
2010-06-02 09:17:45c#串口通信类,实现了多线程技术,和大家共同进步,共同学习 -
基于线程ID的多线程共享集关联缓存的功率降低机制
2021-03-09 13:48:48基于线程ID的多线程共享集关联缓存的功率降低机制 -
双缓存多线程加载数据
2012-09-05 09:45:11双缓存多线程加载数据 -
Java的多线程机制:缓存一致性和CAS
2015-10-18 21:21:57一、总线锁定和缓存一致性 这是两个操作系统层面的概念。随着多核时代的到来,并发操作已经成了很正常的现象,操作系统必须要有一些机制和原语,以保证某些基本操作的原子性,比如处理器需要保证读一个字节或写一... -
安卓Android源码——下载网络图片 (整合多线程、内存缓存、本地文件缓存~) .zip
2021-10-10 21:33:27安卓Android源码——下载网络图片 (整合多线程、内存缓存、本地文件缓存~) .zip -
多线程的队列循环缓存
2016-06-07 11:41:22程序开发中经常遇到一种情况:一个线程收数据(例如socket->recv),一个线程处理数据。 这种情况下,我通常的处理方式是一个公共的buff、一个锁,接收线程收到数据后,加锁拷贝内存记录偏移。另一个线程判断偏移大于... -
安卓开发-下载网络图片 (整合多线程、内存缓存、本地文件缓存~) .zip.zip
2021-11-18 12:57:13安卓开发-下载网络图片 (整合多线程、内存缓存、本地文件缓存~) .zip.zip -
jedis在多线程下的一个大坑
2018-02-11 18:03:06最近使用jedis进行redis的数据操作,发现服务器运行一段时间之后,总是...后来上网查了一下,发现好多人都遇到了同样的问题,原来是jedis操作redis的时候,对底层执行redis命令做了缓存,所以如果某一次redis操作出... -
Android中加载网络资源时的优化可使用(线程+缓存)解决
2021-01-20 10:02:42网上关于这个方面的文章也不少,基本的思路是线程+缓存来解决。下面提出一些优化: 1、采用线程池 2、内存缓存+文件缓存 3、内存缓存中网上很多是采用SoftReference来防止堆溢出,这儿严格限制只能使用最大JVM内存的... -
experimental-mf, 缓存友好的多线程矩阵分解.zip
2019-09-18 04:52:47experimental-mf, 缓存友好的多线程矩阵分解 基于的快速矩阵分解特性缓存友好的多线程矩阵分解。矩阵分解的快速多线程随机梯度动力学( SGLD ) 。快速多线程差分 private 矩阵分解。自适应Regularizer矩阵分解。数据... -
java开启多线程同时查询数据库(线程池+redis缓存优化)
2021-01-05 18:04:38java多线程查询数据库(线程池) 需求介绍: 调用接口后一个页面要展示两个列表,而且数据量很大。 分析: 如果按原始方法进行两次查询再将结果返回当然也是可以的。但是查询时间就是两个查询的和,数据量很小的话... -
线程级缓存ThreadLocalCache
2019-03-21 10:11:24很多时候一条操作链路上需要获取很多重复的基础信息,比如用户的信息,可能在AO层也有,Service层也有,这样造成的问题是每次都需要发起一次调用(数据库orRPC),这样造成的问题是对性能的无谓浪费,当然可以通过... -
万字图解Java多线程
2020-09-06 14:45:07java多线程我个人觉得是javaSe中最难的一部分,我以前也是感觉学会了,但是真正有多线程的需求却不知道怎么下手,实际上还是对多线程这块知识了解不深刻,不知道多线程api的应用场景,不知道多线程的运行流程等等,... -
多线程实现项目初始化完成大规模数据写入redis缓存
2019-02-27 10:42:57**项目中,经常碰见需要在项目初始化时完成从mysql的数据写入到缓存如redis中的操作,例如ssm架构项目中,需要某个类实现一个initializeBean的接口,在这个类里面完成初始化的操作,将mysql的数据写到redis或其他... -
优化版的环形缓存器 线程安全 生产者消费者模式
2016-12-15 13:22:54之前上传过一个环形缓存器(这是一个基本的生产者消费者模式的环形缓存器,具有线程安全性,可同时写入和读取,非装箱拆箱操作,高性能大数据可使用此缓存器。),这个是优化版,增加了缓存器满的标志位,优化了内存... -
Java多线程之线程安全问题
2022-03-31 11:02:50本篇文章介绍的内容为Java多线程中的线程安全问题,此处的安全问题并不是指的像黑客入侵造成的安全问题,线程安全问题是指因多线程抢占式执行而导致程序出现bug的问题。 -
多线程---缓存系统
2012-02-13 21:50:35首先解释下缓存系统: 在程序运行过程中,有些数据我们不会经常修改,例如数据库中性别字段,但是我们却经常使用,如果每次都从数据库中获取,那么将会降低程序性能。那么我们可以在内存中分配一个区域专门存放... -
JAVA多线程-线程安全问题
2022-01-24 19:17:29一、CPU多核缓存架构 CPU分为三级缓存: 每个CPU都有L1,L2缓存,但是L3缓存是多核公用的。 CPU查找数据的顺序为: CPU -> L1 -> L2 -> L3 -> 内存 -> 硬盘 进一步优化,CPU每次读取一个数据,并... -
3. java缓存-线程内缓存guava cache
2018-09-25 16:12:03guava cache的缓存结构 常用的guava cache缓存 根据上图中的缓存框架,我们常用的一些缓存实例有:LocalManualCache和LocalLoadingCache,两者唯一的区别就是LocalLoadingCache extends LocalManualCache ...