mirror of
https://github.com/tiennm99/zfoo.git
synced 2026-08-05 22:25:49 +00:00
fix[cache]: Comparison method violates its general contract
This commit is contained in:
@@ -74,7 +74,7 @@ public class Pair<K, V> {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(key, value);
|
||||
return Objects.hashCode(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The zfoo Authors
|
||||
* 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.zfoo.protocol.model;
|
||||
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 键值对对象,只能在构造时传入键值
|
||||
*
|
||||
* @param <V> 值类型
|
||||
* @author godotg
|
||||
*/
|
||||
public class PairLong<V> {
|
||||
|
||||
private long key;
|
||||
private V value;
|
||||
|
||||
public PairLong() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 构造
|
||||
*
|
||||
* @param key 键
|
||||
* @param value 值
|
||||
*/
|
||||
public PairLong(long key, V value) {
|
||||
this.key = key;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取键
|
||||
*
|
||||
* @return 键
|
||||
*/
|
||||
public long getKey() {
|
||||
return this.key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取值
|
||||
*
|
||||
* @return 值
|
||||
*/
|
||||
public V getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
PairLong<?> pairLong = (PairLong<?>) o;
|
||||
return key == pairLong.key && Objects.equals(value, pairLong.value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Long.hashCode(key);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Pair [key=" + key + ", value=" + value + "]";
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.zfoo.scheduler.util;
|
||||
|
||||
import com.zfoo.protocol.model.Pair;
|
||||
import com.zfoo.protocol.model.PairLong;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -16,7 +17,7 @@ import java.util.function.BiConsumer;
|
||||
public class LazyCache<K, V> {
|
||||
|
||||
private static final float DEFAULT_BACK_PRESSURE_FACTOR = 0.11f;
|
||||
private static final long MILLIS_MAX_SIZE_CHECK_INTERVAL = 13;
|
||||
private static final long MILLIS_MAX_SIZE_CHECK_INTERVAL = 3;
|
||||
|
||||
private static class CacheValue<V> {
|
||||
public volatile V value;
|
||||
@@ -87,6 +88,7 @@ public class LazyCache<K, V> {
|
||||
if (oldCacheValue != null) {
|
||||
removeListener.accept(new Pair<>(key, oldCacheValue.value), RemovalCause.REPLACED);
|
||||
}
|
||||
checkExpire();
|
||||
checkMaximumSize();
|
||||
}
|
||||
|
||||
@@ -134,16 +136,17 @@ public class LazyCache<K, V> {
|
||||
// -----------------------------------------------------------------------------------------------------------------
|
||||
private void checkMaximumSize() {
|
||||
if (cacheMap.size() > backPressureSize) {
|
||||
var now = TimeUtils.currentTimeMillis();
|
||||
var currentTimeMillis = TimeUtils.currentTimeMillis();
|
||||
var sizeCheckTime = sizeCheckTimeAtomic.get();
|
||||
if (now > sizeCheckTime) {
|
||||
if (sizeCheckTimeAtomic.compareAndSet(sizeCheckTime, now + MILLIS_MAX_SIZE_CHECK_INTERVAL)) {
|
||||
if (currentTimeMillis > sizeCheckTime) {
|
||||
if (sizeCheckTimeAtomic.compareAndSet(sizeCheckTime, currentTimeMillis + MILLIS_MAX_SIZE_CHECK_INTERVAL)) {
|
||||
var exceedList = cacheMap.entrySet()
|
||||
.stream()
|
||||
.sorted((a, b) -> Long.compare(a.getValue().expireTime, b.getValue().expireTime))
|
||||
.map(it -> new PairLong<>(it.getValue().expireTime, it.getKey()))
|
||||
.sorted((a, b) -> Long.compare(a.getKey(), b.getKey()))
|
||||
.limit(Math.max(0, cacheMap.size() - maximumSize))
|
||||
.toList();
|
||||
exceedList.forEach(it -> removeForCause(it.getKey(), RemovalCause.SIZE));
|
||||
exceedList.forEach(it -> removeForCause(it.getValue(), RemovalCause.SIZE));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,11 @@ import com.zfoo.protocol.util.StringUtils;
|
||||
import com.zfoo.protocol.util.ThreadUtils;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.function.BiConsumer;
|
||||
|
||||
/**
|
||||
@@ -14,25 +18,36 @@ import java.util.function.BiConsumer;
|
||||
@Ignore
|
||||
public class LazyCacheTest {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(LazyCacheTest.class);
|
||||
|
||||
private static final BiConsumer<Pair<Integer, String>, LazyCache.RemovalCause> myRemoveCallback = new BiConsumer<Pair<Integer, String>, LazyCache.RemovalCause>() {
|
||||
@Override
|
||||
public void accept(Pair<Integer, String> pair, LazyCache.RemovalCause removalCause) {
|
||||
System.out.println(StringUtils.format("remove key:[{}] value:[{}] removalCause:[{}]", pair.getKey(), pair.getValue(), removalCause));
|
||||
logger.info("remove key:[{}] value:[{}] removalCause:[{}]", pair.getKey(), pair.getValue(), removalCause);
|
||||
}
|
||||
};
|
||||
|
||||
@Test
|
||||
public void putTest() {
|
||||
var lazyCache = new LazyCache<Integer, String>(3, 10 * TimeUtils.MILLIS_PER_SECOND, 5 * TimeUtils.MILLIS_PER_SECOND, myRemoveCallback);
|
||||
var lazyCache = new LazyCache<Integer, String>(10, 10 * TimeUtils.MILLIS_PER_SECOND, 5 * TimeUtils.MILLIS_PER_SECOND, myRemoveCallback);
|
||||
|
||||
lazyCache.put(1, "a");
|
||||
lazyCache.put(2, "b");
|
||||
ThreadUtils.sleep(1000);
|
||||
lazyCache.put(3, "c");
|
||||
ThreadUtils.sleep(1000);
|
||||
lazyCache.put(4, "d");
|
||||
ThreadUtils.sleep(1000);
|
||||
lazyCache.put(5, "e");
|
||||
lazyCache.put(6, "f");
|
||||
lazyCache.put(7, "g");
|
||||
lazyCache.put(8, "h");
|
||||
lazyCache.put(9, "i");
|
||||
lazyCache.put(10, "j");
|
||||
lazyCache.put(11, "k");
|
||||
lazyCache.put(12, "l");
|
||||
ThreadUtils.sleep(1000);
|
||||
lazyCache.put(13, "m");
|
||||
ThreadUtils.sleep(1000);
|
||||
lazyCache.put(14, "n");
|
||||
ThreadUtils.sleep(1000);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -84,4 +99,35 @@ public class LazyCacheTest {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void multipleThreadTest() {
|
||||
int threadNum = Runtime.getRuntime().availableProcessors() + 1;
|
||||
ExecutorService[] executors = new ExecutorService[threadNum];
|
||||
for (int i = 0; i < executors.length; i++) {
|
||||
executors[i] = Executors.newSingleThreadExecutor();
|
||||
}
|
||||
var lazyCache = new LazyCache<Integer, String>(1_0000, 10 * TimeUtils.MILLIS_PER_SECOND, 5 * TimeUtils.MILLIS_PER_SECOND, myRemoveCallback);
|
||||
for (int i = 0; i < executors.length; i++) {
|
||||
|
||||
var executor = executors[i];
|
||||
int i1 = i;
|
||||
executor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
var startIndex = i1 * 1_0000;
|
||||
for (int j = i1 * 1_0000; j < startIndex + 1_0000; j++) {
|
||||
lazyCache.put(j, String.valueOf(j));
|
||||
}
|
||||
for (int j = 0; j < 10000; j++) {
|
||||
lazyCache.get(j);
|
||||
ThreadUtils.sleep(1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
ThreadUtils.sleep(Long.MAX_VALUE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user