修复StampedCache类get方法并发问题(issue#IBCIQG@Gitee)

This commit is contained in:
Looly
2024-12-21 00:46:34 +08:00
parent 63c3777c70
commit 738e965a06
8 changed files with 59 additions and 41 deletions

View File

@@ -198,7 +198,7 @@ public class StrictBeanDesc extends AbstractBeanDesc {
private static Method getGetterForBoolean(final Method[] gettersOrSetters, final String fieldName, final boolean ignoreCase) {
// 查找isXXX
return MethodUtil.getMethod(gettersOrSetters, m -> {
if (0 != m.getParameterCount() || false == BooleanUtil.isBoolean(m.getReturnType())) {
if (0 != m.getParameterCount() || !BooleanUtil.isBoolean(m.getReturnType())) {
// getter方法要求无参数且返回boolean或Boolean
return false;
}
@@ -231,7 +231,7 @@ public class StrictBeanDesc extends AbstractBeanDesc {
private static Method getSetterForBoolean(final Method[] gettersOrSetters, final String fieldName, final boolean ignoreCase) {
// 查找isXXX
return MethodUtil.getMethod(gettersOrSetters, m -> {
if (1 != m.getParameterCount() || false == BooleanUtil.isBoolean(m.getParameterTypes()[0])) {
if (1 != m.getParameterCount() || !BooleanUtil.isBoolean(m.getParameterTypes()[0])) {
// setter方法要求1个boolean或Boolean参数
return false;
}

View File

@@ -51,12 +51,12 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V> {
@Override
public boolean containsKey(final K key) {
return null != get(key, false, false);
return null != doGet(key, false, false);
}
@Override
public V get(final K key, final boolean isUpdateLastAccess) {
return get(key, isUpdateLastAccess, true);
return doGet(key, isUpdateLastAccess, true);
}
@Override
@@ -106,19 +106,38 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V> {
}
/**
* 获取值
* 获取值,使用乐观锁,但是此方法可能导致读取脏数据,但对于缓存业务可容忍。情况如下:
* <pre>
* 1. 读取时无写入,不冲突,直接获取值
* 2. 读取时无写入,但是乐观读时触发了并发异常,此时获取同步锁,获取新值
* 4. 读取时有写入,此时获取同步锁,获取新值
* </pre>
*
* @param key 键
* @param isUpdateLastAccess 是否更新最后修改时间
* @param isUpdateCount 是否更新命中数get时更新contains时不更新
* @return 值或null
*/
private V get(final K key, final boolean isUpdateLastAccess, final boolean isUpdateCount) {
private V doGet(final K key, final boolean isUpdateLastAccess, final boolean isUpdateCount) {
// 尝试读取缓存,使用乐观读锁
CacheObj<K, V> co = null;
long stamp = lock.tryOptimisticRead();
CacheObj<K, V> co = getWithoutLock(key);
if (false == lock.validate(stamp)) {
// 有写线程修改了此对象,悲观读
boolean isReadError = true;
if(lock.validate(stamp)){
try{
// 乐观读,可能读取脏数据,在缓存中可容忍,分两种情况
// 1. 读取时无线程写入
// 2. 读取时有线程写入,导致数据不一致,此时读取未更新的缓存值
co = getWithoutLock(key);
isReadError = false;
} catch (final Exception ignore){
// ignore
}
}
if(isReadError){
// 转换为悲观读
// 原因可能为无锁读时触发并发异常,或者锁被占(正在写)
stamp = lock.readLock();
try {
co = getWithoutLock(key);
@@ -133,7 +152,7 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V> {
missCount.increment();
}
return null;
} else if (false == co.isExpired()) {
} else if (!co.isExpired()) {
if (isUpdateCount) {
hitCount.increment();
}
@@ -159,7 +178,7 @@ public abstract class StampedCache<K, V> extends AbstractCache<K, V> {
if (null == co) {
return null;
}
if (false == co.isExpired()) {
if (!co.isExpired()) {
// 首先尝试获取值,如果值存在且有效,返回之
if (isUpdateCount) {
hitCount.increment();

View File

@@ -152,29 +152,30 @@ public class DateBetween implements Serializable {
final Calendar endCal = CalendarUtil.calendar(end);
final int result = endCal.get(Calendar.YEAR) - beginCal.get(Calendar.YEAR);
if (false == isReset) {
final int beginMonthBase0 = beginCal.get(Calendar.MONTH);
final int endMonthBase0 = endCal.get(Calendar.MONTH);
if (beginMonthBase0 < endMonthBase0) {
return result;
} else if (beginMonthBase0 > endMonthBase0) {
return result - 1;
} else if (Calendar.FEBRUARY == beginMonthBase0
&& CalendarUtil.isLastDayOfMonth(beginCal)
&& CalendarUtil.isLastDayOfMonth(endCal)) {
// 考虑闰年的2月情况
// 两个日期都位于2月的最后一天此时月数按照相等对待此时都设置为1号
beginCal.set(Calendar.DAY_OF_MONTH, 1);
endCal.set(Calendar.DAY_OF_MONTH, 1);
}
endCal.set(Calendar.YEAR, beginCal.get(Calendar.YEAR));
final long between = endCal.getTimeInMillis() - beginCal.getTimeInMillis();
if (between < 0) {
return result - 1;
}
if(isReset){
return result;
}
final int beginMonthBase0 = beginCal.get(Calendar.MONTH);
final int endMonthBase0 = endCal.get(Calendar.MONTH);
if (beginMonthBase0 < endMonthBase0) {
return result;
} else if (beginMonthBase0 > endMonthBase0) {
return result - 1;
} else if (Calendar.FEBRUARY == beginMonthBase0
&& CalendarUtil.isLastDayOfMonth(beginCal)
&& CalendarUtil.isLastDayOfMonth(endCal)) {
// 考虑闰年的2月情况
// 两个日期都位于2月的最后一天此时月数按照相等对待此时都设置为1号
beginCal.set(Calendar.DAY_OF_MONTH, 1);
endCal.set(Calendar.DAY_OF_MONTH, 1);
}
endCal.set(Calendar.YEAR, beginCal.get(Calendar.YEAR));
final long between = endCal.getTimeInMillis() - beginCal.getTimeInMillis();
if (between < 0) {
return result - 1;
}
return result;
}
/**

View File

@@ -221,7 +221,7 @@ public class FileTypeUtil {
* @throws IORuntimeException 读取文件引起的异常
*/
public static String getType(final File file, final boolean isExact) throws IORuntimeException {
if (false == FileUtil.isFile(file)) {
if (!FileUtil.isFile(file)) {
throw new IllegalArgumentException("Not a regular file!");
}
InputStream in = null;

View File

@@ -584,9 +584,7 @@ public class FileUtil {
* @since 5.8.28
*/
public static int getTotalLines(final File file, final int bufferSize) {
if (false == isFile(file)) {
throw new IORuntimeException("Input must be a File");
}
Assert.isTrue(isFile(file), ()-> new IORuntimeException("Input must be a File"));
try (final LineCounter lineCounter = new LineCounter(getInputStream(file), bufferSize)) {
return lineCounter.getCount();
} catch (final IOException e) {
@@ -778,7 +776,7 @@ public class FileUtil {
* @since 4.5.5
*/
public static boolean cleanEmpty(final File directory) throws IORuntimeException {
if (directory == null || false == directory.exists() || false == directory.isDirectory()) {
if (directory == null || !directory.exists() || !directory.isDirectory()) {
return true;
}