
cn.taketoday.bytecode.core.LoadingCache Maven / Gradle / Ivy
/*
* Original Author -> Harry Yang ([email protected]) https://taketoday.cn
* Copyright © TODAY & 2017 - 2022 All Rights Reserved.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see [http://www.gnu.org/licenses/]
*/
package cn.taketoday.bytecode.core;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import java.util.function.Function;
/**
* @author TODAY
* 2019-09-01 22:04
*/
@SuppressWarnings({ "unchecked" })
final class LoadingCache {
public final Function loader;
public final Function keyMapper;
public final ConcurrentHashMap map = new ConcurrentHashMap<>();
public LoadingCache(Function keyMapper, Function loader) {
this.loader = loader;
this.keyMapper = keyMapper;
}
public V get(K key) {
final KK cacheKey = keyMapper.apply(key);
Object v = map.get(cacheKey);
if (v != null && !(v instanceof FutureTask)) {
return (V) v;
}
return createEntry(key, cacheKey, v);
}
/**
* Loads entry to the cache. If entry is missing, put {@link FutureTask} first
* so other competing thread might wait for the result.
*
* @param key original key that would be used to load the instance
* @param cacheKey key that would be used to store the entry in internal map
* @param v null or {@link FutureTask}
* @return newly created instance
*/
private V createEntry(final K key, KK cacheKey, Object v) {
FutureTask task;
boolean created = false;
if (v != null) {
// Another thread is already loading an instance
task = (FutureTask) v;
}
else {
task = new FutureTask<>(() -> loader.apply(key));
Object prevTask = map.putIfAbsent(cacheKey, task);
if (prevTask == null) {
// creator does the load
created = true;
task.run();
}
else if (prevTask instanceof FutureTask) {
task = (FutureTask) prevTask;
}
else {
return (V) prevTask;
}
}
try {
V result = task.get();
if (created) {
map.put(cacheKey, result);
}
return result;
}
catch (InterruptedException e) {
throw new IllegalStateException("Interrupted while loading cache item", e);
}
catch (ExecutionException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw ((RuntimeException) cause);
}
throw new IllegalStateException("Unable to load cache item", cause);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy