mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
修复StampedCache类get方法并发问题(issue#IBCIQG@Gitee)
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
@@ -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();
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@@ -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;
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user