我在努力学习并发
我在玩下面的代码:
class LRUCache {
/**
Algorithm :
1. Everytime when we add the node check if it exists .
1.1 if exists then update the value .
1.2 move this node to head.
1.3 if the capaicity reaches then remove node from tail .
1.4 move the current node to head.
2. Everytime when we get
2.1 then we need to move this node to head.
3. Remove the node from tail thats it.
}
**/
private int capacity;
private AtomicInteger curSize = new AtomicInteger() ;
private ConcurrentHashMap<Integer,Node> map = new ConcurrentHashMap<>();
private Node head = new Node();
private Node tail = new Node();
private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public LRUCache(int capacity) {
this.capacity = capacity;
head.next = tail;
tail.prev = head;
}
public int get(int key) {
if(!map.containsKey(key))
return -1;
Node node = map.get(key);
// move this current Node to front
moveToHead(node);
return node.val;
}
public void put(int key, int value) {
// The key is already present
Node curNode = map.get(key);
if(curNode != null){
// if value exist update
curNode.val=value;
// moveTo head , so now its used recently
moveToHead(curNode);
return;
}
Node newNode = new Node(value,key);
map.put(key,newNode);
addToHead(newNode);
if( curSize.incrementAndGet() > capacity){
Node nodeToRemove = tail.prev;
removeNode(nodeToRemove);
map.remove(nodeToRemove.key);
curSize.decrementAndGet();
}
}
private void moveToHead(Node node){
// remove
removeNode(node);
addToHead(node);
}
private void removeNode(Node node){
try{
readWriteLock.writeLock().lock();
Node prev = node.prev;
Node next = node.next;
prev.next = next;
next.prev = prev;
}finally{
readWriteLock.writeLock().unlock();
}
}
private void addToHead(Node node){
try{
readWriteLock.writeLock().lock();
node.next = head.next;
node.prev = head;
head.next.prev = node;
head.next = node;
}finally{
readWriteLock.writeLock().unlock();
}
}
}
class Node {
Integer val;
Integer key;
Node next ;
Node prev;
public Node(int val, int key){
this.val = val;
this.key = key;
}
public Node(){
}
}
/**
* Your LRUCache object will be instantiated and called as such:
* LRUCache obj = new LRUCache(capacity);
* int param_1 = obj.get(key);
* obj.put(key,value);
*/我观察到get方法没有任何线程安全,所以我就这样修改了它。
public int get(int key) {
readWriteLock.readLock().lock();
try{
if(!map.containsKey(key))
return -1;
Node node = map.get(key);
// move this current Node to front
moveToHead(node);
return node.val;
}finally{
readWriteLock.readLock().unlock();
}
}我认为这将确保,reads不会在writes运行时发生。
但是在修改get函数之后,在Leetcode中我得到了超过时间限制的信息。谁能解释一下原因吗?
发布于 2021-06-27 08:16:24
我在努力学习并发 我在玩下面的代码:
我强烈建议您不要从这段代码中学习并发性,因为它充满了各种并发错误。
从这种天真的错误中:
if(!map.containsKey(key))
return -1;
Node node = map.get(key);
// move this current Node to front
moveToHead(node);
return node.val;在containsKey之后和get之前,另一个线程可能会删除密钥,因此,我们将得到node==null和NullPointerExcetion。
对于更多特定于java的错误,如以下所示:
curNode.val=value;在这里,新值被写到val而没有适当的同步,这就创建了一个所谓的数据竞争(即在其他线程中读取val将返回奇怪的结果)。
修复代码的最简单方法是使每个公共方法都是synchronized,并丢弃readWriteLock。
同样,在这种情况下,将AtomicInteger和ConcurrentHashMap替换为int和HashMap是合理的(您不需要使用synchronized的并发版本)。
如果您想在java中学习并发性,那么我建议:
AtomicInteger这样的东西是真正存在的)。https://stackoverflow.com/questions/68148385
复制相似问题