com.gitee.starblues.loader.classloader.resource.cache.LRUMapCache Maven / Gradle / Ivy
/**
* Copyright [2019-Present] [starBlues]
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gitee.starblues.loader.classloader.resource.cache;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.locks.StampedLock;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* LRU 缓存实现
*
* @author starBlues
* @since 3.1.1
* @version 3.1.1
*/
public class LRUMapCache implements Cache{
private final Map> cacheMap;
private final StampedLock lock = new StampedLock();
private final int size;
private final long timeout;
public LRUMapCache(int size, long timeout){
this.size = size;
this.timeout = timeout;
this.cacheMap = new CacheLinkedHashMap>(size);
}
@Override
public void put(K key, V value) {
long stamp = lock.writeLock();
try {
Entity entity = new Entity<>(value, timeout);
if (isFull(key)) {
cleanExpired(false);
}
cacheMap.put(key, entity);
} finally {
lock.unlockWrite(stamp);
}
}
@Override
public int size() {
long stamp = lock.tryOptimisticRead();
int size = cacheMap.size();
if(!lock.validate(stamp)){
stamp = lock.readLock();
try {
size = cacheMap.size();
} finally {
lock.unlockRead(stamp);
}
}
return size;
}
@Override
public boolean containsKey(K key) {
return get(key) != null;
}
@Override
public V get(K key) {
long stamp = lock.tryOptimisticRead();
Entity entity = cacheMap.get(key);
if(!lock.validate(stamp)){
stamp = lock.readLock();
try {
entity = cacheMap.get(key);
} finally {
lock.unlockRead(stamp);
}
}
if(entity != null){
if(entity.isExpired()){
remove(key);
return null;
}
return entity.getValue();
}
return null;
}
@Override
public V getOrDefault(K key, Supplier supplier, boolean defaultAdded) {
long stamp = lock.tryOptimisticRead();
Entity entity = cacheMap.get(key);
if(!lock.validate(stamp)){
stamp = lock.readLock();
try {
entity = cacheMap.get(key);
} finally {
lock.unlockRead(stamp);
}
}
if(entity != null){
if(entity.isExpired()){
remove(key);
} else {
return entity.getValue();
}
}
V v = supplier.get();
if(v != null){
if(defaultAdded){
put(key, v);
}
}
return v;
}
@Override
public V remove(K key) {
long stamp = lock.writeLock();
try {
Entity cacheValue = cacheMap.remove(key);
if (cacheValue != null) {
return cacheValue.getValue();
} else {
return null;
}
} finally {
lock.unlockWrite(stamp);
}
}
@Override
public void clear() {
clear(null);
}
@Override
public void clear(Consumer consumer) {
long stamp = lock.writeLock();
try {
if(consumer == null){
cacheMap.clear();
return;
}
Iterator>> iterator = cacheMap.entrySet().iterator();
while (iterator.hasNext()){
try {
Map.Entry> entityEntry = iterator.next();
Entity value = entityEntry.getValue();
if(value == null){
iterator.remove();
continue;
}
V v = value.getValue();
if(v == null){
iterator.remove();
continue;
}
consumer.accept(v);
} catch (Exception e){
// 忽略
}
}
} finally {
lock.unlockWrite(stamp);
}
}
@Override
public int cleanExpired() {
return cleanExpired(true);
}
private boolean isFull(K key) {
if (size == 0) {
return false;
}
if(cacheMap.size() < size){
return false;
}
return !cacheMap.containsKey(key);
}
public int cleanExpired(boolean isLock) {
if(!isLock){
return actualCleanExpired();
}
long stamp = lock.writeLock();
try {
return actualCleanExpired();
} finally {
lock.unlockWrite(stamp);
}
}
private int actualCleanExpired(){
Iterator>> cacheMapIterator = cacheMap.entrySet().iterator();
int removeCount = 0;
while (cacheMapIterator.hasNext()){
Map.Entry> entityMap = cacheMapIterator.next();
Entity value = entityMap.getValue();
if(value == null || value.isExpired()){
cacheMapIterator.remove();
removeCount++;
}
}
return removeCount;
}
}