Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ma yanling
hutool-5-master
Commits
45cda665
Commit
45cda665
authored
Sep 25, 2024
by
ma yanling
Browse files
project commit
parent
ad2fb30a
Pipeline
#2354
failed with stages
in 0 seconds
Changes
369
Pipelines
1
Show whitespace changes
Inline
Side-by-side
Showing
20 changed files
with
1782 additions
and
0 deletions
+1782
-0
hutool-cache/src/main/java/cn/hutool/cache/GlobalPruneTimer.java
...cache/src/main/java/cn/hutool/cache/GlobalPruneTimer.java
+82
-0
hutool-cache/src/main/java/cn/hutool/cache/file/AbstractFileCache.java
...src/main/java/cn/hutool/cache/file/AbstractFileCache.java
+134
-0
hutool-cache/src/main/java/cn/hutool/cache/file/LFUFileCache.java
...ache/src/main/java/cn/hutool/cache/file/LFUFileCache.java
+63
-0
hutool-cache/src/main/java/cn/hutool/cache/file/LRUFileCache.java
...ache/src/main/java/cn/hutool/cache/file/LRUFileCache.java
+63
-0
hutool-cache/src/main/java/cn/hutool/cache/file/package-info.java
...ache/src/main/java/cn/hutool/cache/file/package-info.java
+7
-0
hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java
...che/src/main/java/cn/hutool/cache/impl/AbstractCache.java
+269
-0
hutool-cache/src/main/java/cn/hutool/cache/impl/CacheObj.java
...ol-cache/src/main/java/cn/hutool/cache/impl/CacheObj.java
+134
-0
hutool-cache/src/main/java/cn/hutool/cache/impl/CacheObjIterator.java
.../src/main/java/cn/hutool/cache/impl/CacheObjIterator.java
+73
-0
hutool-cache/src/main/java/cn/hutool/cache/impl/CacheValuesIterator.java
...c/main/java/cn/hutool/cache/impl/CacheValuesIterator.java
+48
-0
hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java
...l-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java
+80
-0
hutool-cache/src/main/java/cn/hutool/cache/impl/LFUCache.java
...ol-cache/src/main/java/cn/hutool/cache/impl/LFUCache.java
+95
-0
hutool-cache/src/main/java/cn/hutool/cache/impl/LRUCache.java
...ol-cache/src/main/java/cn/hutool/cache/impl/LRUCache.java
+78
-0
hutool-cache/src/main/java/cn/hutool/cache/impl/NoCache.java
hutool-cache/src/main/java/cn/hutool/cache/impl/NoCache.java
+117
-0
hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java
...he/src/main/java/cn/hutool/cache/impl/ReentrantCache.java
+146
-0
hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java
...ache/src/main/java/cn/hutool/cache/impl/StampedCache.java
+141
-0
hutool-cache/src/main/java/cn/hutool/cache/impl/TimedCache.java
...-cache/src/main/java/cn/hutool/cache/impl/TimedCache.java
+88
-0
hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java
...l-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java
+43
-0
hutool-cache/src/main/java/cn/hutool/cache/impl/package-info.java
...ache/src/main/java/cn/hutool/cache/impl/package-info.java
+7
-0
hutool-cache/src/main/java/cn/hutool/cache/package-info.java
hutool-cache/src/main/java/cn/hutool/cache/package-info.java
+7
-0
hutool-cache/src/test/java/cn/hutool/cache/CacheConcurrentTest.java
...he/src/test/java/cn/hutool/cache/CacheConcurrentTest.java
+107
-0
No files found.
Too many changes to show.
To preserve performance only
369 of 369+
files are displayed.
Plain diff
Email patch
hutool-cache/src/main/java/cn/hutool/cache/GlobalPruneTimer.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache
;
import
cn.hutool.core.thread.ThreadUtil
;
import
cn.hutool.core.util.StrUtil
;
import
java.util.List
;
import
java.util.concurrent.ScheduledExecutorService
;
import
java.util.concurrent.ScheduledFuture
;
import
java.util.concurrent.ScheduledThreadPoolExecutor
;
import
java.util.concurrent.TimeUnit
;
import
java.util.concurrent.atomic.AtomicInteger
;
/**
* 全局缓存清理定时器池,用于在需要过期支持的缓存对象中超时任务池
*
* @author looly
*/
public
enum
GlobalPruneTimer
{
/**
* 单例对象
*/
INSTANCE
;
/**
* 缓存任务计数
*/
private
final
AtomicInteger
cacheTaskNumber
=
new
AtomicInteger
(
1
);
/**
* 定时器
*/
private
ScheduledExecutorService
pruneTimer
;
/**
* 构造
*/
GlobalPruneTimer
()
{
create
();
}
/**
* 启动定时任务
*
* @param task 任务
* @param delay 周期
* @return {@link ScheduledFuture}对象,可手动取消此任务
*/
public
ScheduledFuture
<?>
schedule
(
Runnable
task
,
long
delay
)
{
return
this
.
pruneTimer
.
scheduleAtFixedRate
(
task
,
delay
,
delay
,
TimeUnit
.
MILLISECONDS
);
}
/**
* 创建定时器
*/
public
void
create
()
{
if
(
null
!=
pruneTimer
)
{
shutdownNow
();
}
this
.
pruneTimer
=
new
ScheduledThreadPoolExecutor
(
1
,
r
->
ThreadUtil
.
newThread
(
r
,
StrUtil
.
format
(
"Pure-Timer-{}"
,
cacheTaskNumber
.
getAndIncrement
())));
}
/**
* 销毁全局定时器
*/
public
void
shutdown
()
{
if
(
null
!=
pruneTimer
)
{
pruneTimer
.
shutdown
();
}
}
/**
* 销毁全局定时器
*
* @return 销毁时未被执行的任务列表
*/
public
List
<
Runnable
>
shutdownNow
()
{
if
(
null
!=
pruneTimer
)
{
return
pruneTimer
.
shutdownNow
();
}
return
null
;
}
}
hutool-cache/src/main/java/cn/hutool/cache/file/AbstractFileCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.file
;
import
java.io.File
;
import
java.io.Serializable
;
import
cn.hutool.cache.Cache
;
import
cn.hutool.core.io.FileUtil
;
import
cn.hutool.core.io.IORuntimeException
;
/**
* 文件缓存,以解决频繁读取文件引起的性能问题
* @author Looly
*
*/
public
abstract
class
AbstractFileCache
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
/** 容量 */
protected
final
int
capacity
;
/** 缓存的最大文件大小,文件大于此大小时将不被缓存 */
protected
final
int
maxFileSize
;
/** 默认超时时间,0表示无默认超时 */
protected
final
long
timeout
;
/** 缓存实现 */
protected
final
Cache
<
File
,
byte
[]>
cache
;
/** 已使用缓存空间 */
protected
int
usedSize
;
/**
* 构造
* @param capacity 缓存容量
* @param maxFileSize 文件最大大小
* @param timeout 默认超时时间,0表示无默认超时
*/
public
AbstractFileCache
(
int
capacity
,
int
maxFileSize
,
long
timeout
)
{
this
.
capacity
=
capacity
;
this
.
maxFileSize
=
maxFileSize
;
this
.
timeout
=
timeout
;
this
.
cache
=
initCache
();
}
/**
* @return 缓存容量(byte数)
*/
public
int
capacity
()
{
return
capacity
;
}
/**
* @return 已使用空间大小(byte数)
*/
public
int
getUsedSize
()
{
return
usedSize
;
}
/**
* @return 允许被缓存文件的最大byte数
*/
public
int
maxFileSize
()
{
return
maxFileSize
;
}
/**
* @return 缓存的文件数
*/
public
int
getCachedFilesCount
()
{
return
cache
.
size
();
}
/**
* @return 超时时间
*/
public
long
timeout
()
{
return
this
.
timeout
;
}
/**
* 清空缓存
*/
public
void
clear
()
{
cache
.
clear
();
usedSize
=
0
;
}
// ---------------------------------------------------------------- get
/**
* 获得缓存过的文件bytes
* @param path 文件路径
* @return 缓存过的文件bytes
* @throws IORuntimeException IO异常
*/
public
byte
[]
getFileBytes
(
String
path
)
throws
IORuntimeException
{
return
getFileBytes
(
new
File
(
path
));
}
/**
* 获得缓存过的文件bytes
* @param file 文件
* @return 缓存过的文件bytes
* @throws IORuntimeException IO异常
*/
public
byte
[]
getFileBytes
(
File
file
)
throws
IORuntimeException
{
byte
[]
bytes
=
cache
.
get
(
file
);
if
(
bytes
!=
null
)
{
return
bytes
;
}
// add file
bytes
=
FileUtil
.
readBytes
(
file
);
if
((
maxFileSize
!=
0
)
&&
(
file
.
length
()
>
maxFileSize
))
{
//大于缓存空间,不缓存,直接返回
return
bytes
;
}
usedSize
+=
bytes
.
length
;
//文件放入缓存,如果usedSize > capacity,purge()方法将被调用
cache
.
put
(
file
,
bytes
);
return
bytes
;
}
// ---------------------------------------------------------------- protected method start
/**
* 初始化实现文件缓存的缓存对象
* @return {@link Cache}
*/
protected
abstract
Cache
<
File
,
byte
[]>
initCache
();
// ---------------------------------------------------------------- protected method end
}
hutool-cache/src/main/java/cn/hutool/cache/file/LFUFileCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.file
;
import
java.io.File
;
import
cn.hutool.cache.Cache
;
import
cn.hutool.cache.impl.LFUCache
;
/**
* 使用LFU缓存文件,以解决频繁读取文件引起的性能问题
* @author Looly
*
*/
public
class
LFUFileCache
extends
AbstractFileCache
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 构造<br>
* 最大文件大小为缓存容量的一半<br>
* 默认无超时
* @param capacity 缓存容量
*/
public
LFUFileCache
(
int
capacity
)
{
this
(
capacity
,
capacity
/
2
,
0
);
}
/**
* 构造<br>
* 默认无超时
* @param capacity 缓存容量
* @param maxFileSize 最大文件大小
*/
public
LFUFileCache
(
int
capacity
,
int
maxFileSize
)
{
this
(
capacity
,
maxFileSize
,
0
);
}
/**
* 构造
* @param capacity 缓存容量
* @param maxFileSize 文件最大大小
* @param timeout 默认超时时间,0表示无默认超时
*/
public
LFUFileCache
(
int
capacity
,
int
maxFileSize
,
long
timeout
)
{
super
(
capacity
,
maxFileSize
,
timeout
);
}
@Override
protected
Cache
<
File
,
byte
[]>
initCache
()
{
return
new
LFUCache
<
File
,
byte
[]>(
LFUFileCache
.
this
.
capacity
,
LFUFileCache
.
this
.
timeout
)
{
private
static
final
long
serialVersionUID
=
1L
;
@Override
public
boolean
isFull
()
{
return
LFUFileCache
.
this
.
usedSize
>
this
.
capacity
;
}
@Override
protected
void
onRemove
(
File
key
,
byte
[]
cachedObject
)
{
usedSize
-=
cachedObject
.
length
;
}
};
}
}
hutool-cache/src/main/java/cn/hutool/cache/file/LRUFileCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.file
;
import
java.io.File
;
import
cn.hutool.cache.Cache
;
import
cn.hutool.cache.impl.LRUCache
;
/**
* 使用LRU缓存文件,以解决频繁读取文件引起的性能问题
* @author Looly
*
*/
public
class
LRUFileCache
extends
AbstractFileCache
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 构造<br>
* 最大文件大小为缓存容量的一半<br>
* 默认无超时
* @param capacity 缓存容量
*/
public
LRUFileCache
(
int
capacity
)
{
this
(
capacity
,
capacity
/
2
,
0
);
}
/**
* 构造<br>
* 默认无超时
* @param capacity 缓存容量
* @param maxFileSize 最大文件大小
*/
public
LRUFileCache
(
int
capacity
,
int
maxFileSize
)
{
this
(
capacity
,
maxFileSize
,
0
);
}
/**
* 构造
* @param capacity 缓存容量
* @param maxFileSize 文件最大大小
* @param timeout 默认超时时间,0表示无默认超时
*/
public
LRUFileCache
(
int
capacity
,
int
maxFileSize
,
long
timeout
)
{
super
(
capacity
,
maxFileSize
,
timeout
);
}
@Override
protected
Cache
<
File
,
byte
[]>
initCache
()
{
return
new
LRUCache
<
File
,
byte
[]>(
LRUFileCache
.
this
.
capacity
,
super
.
timeout
)
{
private
static
final
long
serialVersionUID
=
1L
;
@Override
public
boolean
isFull
()
{
return
LRUFileCache
.
this
.
usedSize
>
this
.
capacity
;
}
@Override
protected
void
onRemove
(
File
key
,
byte
[]
cachedObject
)
{
usedSize
-=
cachedObject
.
length
;
}
};
}
}
hutool-cache/src/main/java/cn/hutool/cache/file/package-info.java
0 → 100644
View file @
45cda665
/**
* 提供针对文件的缓存实现
*
* @author looly
*
*/
package
cn.hutool.cache.file
;
\ No newline at end of file
hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.impl
;
import
cn.hutool.cache.Cache
;
import
cn.hutool.cache.CacheListener
;
import
cn.hutool.core.lang.func.Func0
;
import
cn.hutool.core.lang.mutable.Mutable
;
import
cn.hutool.core.lang.mutable.MutableObj
;
import
cn.hutool.core.map.SafeConcurrentHashMap
;
import
java.util.Iterator
;
import
java.util.Map
;
import
java.util.Set
;
import
java.util.concurrent.atomic.LongAdder
;
import
java.util.concurrent.locks.Lock
;
import
java.util.concurrent.locks.ReentrantLock
;
import
java.util.stream.Collectors
;
/**
* 超时和限制大小的缓存的默认实现<br>
* 继承此抽象缓存需要:<br>
* <ul>
* <li>创建一个新的Map</li>
* <li>实现 {@code prune} 策略</li>
* </ul>
*
* @param <K> 键类型
* @param <V> 值类型
* @author Looly, jodd
*/
public
abstract
class
AbstractCache
<
K
,
V
>
implements
Cache
<
K
,
V
>
{
private
static
final
long
serialVersionUID
=
1L
;
protected
Map
<
Mutable
<
K
>,
CacheObj
<
K
,
V
>>
cacheMap
;
/**
* 写的时候每个key一把锁,降低锁的粒度
*/
protected
final
SafeConcurrentHashMap
<
K
,
Lock
>
keyLockMap
=
new
SafeConcurrentHashMap
<>();
/**
* 返回缓存容量,{@code 0}表示无大小限制
*/
protected
int
capacity
;
/**
* 缓存失效时长, {@code 0} 表示无限制,单位毫秒
*/
protected
long
timeout
;
/**
* 每个对象是否有单独的失效时长,用于决定清理过期对象是否有必要。
*/
protected
boolean
existCustomTimeout
;
/**
* 命中数,即命中缓存计数
*/
protected
LongAdder
hitCount
=
new
LongAdder
();
/**
* 丢失数,即未命中缓存计数
*/
protected
LongAdder
missCount
=
new
LongAdder
();
/**
* 缓存监听
*/
protected
CacheListener
<
K
,
V
>
listener
;
// ---------------------------------------------------------------- put start
@Override
public
void
put
(
K
key
,
V
object
)
{
put
(
key
,
object
,
timeout
);
}
/**
* 加入元素,无锁
*
* @param key 键
* @param object 值
* @param timeout 超时时长
* @since 4.5.16
*/
protected
void
putWithoutLock
(
K
key
,
V
object
,
long
timeout
)
{
CacheObj
<
K
,
V
>
co
=
new
CacheObj
<>(
key
,
object
,
timeout
);
if
(
timeout
!=
0
)
{
existCustomTimeout
=
true
;
}
if
(
isFull
())
{
pruneCache
();
}
cacheMap
.
put
(
MutableObj
.
of
(
key
),
co
);
}
// ---------------------------------------------------------------- put end
// ---------------------------------------------------------------- get start
/**
* @return 命中数
*/
public
long
getHitCount
()
{
return
hitCount
.
sum
();
}
/**
* @return 丢失数
*/
public
long
getMissCount
()
{
return
missCount
.
sum
();
}
@Override
public
V
get
(
K
key
,
boolean
isUpdateLastAccess
,
Func0
<
V
>
supplier
)
{
V
v
=
get
(
key
,
isUpdateLastAccess
);
if
(
null
==
v
&&
null
!=
supplier
)
{
//每个key单独获取一把锁,降低锁的粒度提高并发能力,see pr#1385@Github
final
Lock
keyLock
=
keyLockMap
.
computeIfAbsent
(
key
,
k
->
new
ReentrantLock
());
keyLock
.
lock
();
try
{
// 双重检查锁,防止在竞争锁的过程中已经有其它线程写入
final
CacheObj
<
K
,
V
>
co
=
getWithoutLock
(
key
);
if
(
null
==
co
||
co
.
isExpired
())
{
try
{
v
=
supplier
.
call
();
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
}
put
(
key
,
v
,
this
.
timeout
);
}
else
{
v
=
co
.
get
(
isUpdateLastAccess
);
}
}
finally
{
keyLock
.
unlock
();
keyLockMap
.
remove
(
key
);
}
}
return
v
;
}
/**
* 获取键对应的{@link CacheObj}
* @param key 键,实际使用时会被包装为{@link MutableObj}
* @return {@link CacheObj}
* @since 5.8.0
*/
protected
CacheObj
<
K
,
V
>
getWithoutLock
(
K
key
){
return
this
.
cacheMap
.
get
(
MutableObj
.
of
(
key
));
}
// ---------------------------------------------------------------- get end
@Override
public
Iterator
<
V
>
iterator
()
{
CacheObjIterator
<
K
,
V
>
copiedIterator
=
(
CacheObjIterator
<
K
,
V
>)
this
.
cacheObjIterator
();
return
new
CacheValuesIterator
<>(
copiedIterator
);
}
// ---------------------------------------------------------------- prune start
/**
* 清理实现<br>
* 子类实现此方法时无需加锁
*
* @return 清理数
*/
protected
abstract
int
pruneCache
();
// ---------------------------------------------------------------- prune end
// ---------------------------------------------------------------- common start
@Override
public
int
capacity
()
{
return
capacity
;
}
/**
* @return 默认缓存失效时长。<br>
* 每个对象可以单独设置失效时长
*/
@Override
public
long
timeout
()
{
return
timeout
;
}
/**
* 只有设置公共缓存失效时长或每个对象单独的失效时长时清理可用
*
* @return 过期对象清理是否可用,内部使用
*/
protected
boolean
isPruneExpiredActive
()
{
return
(
timeout
!=
0
)
||
existCustomTimeout
;
}
@Override
public
boolean
isFull
()
{
return
(
capacity
>
0
)
&&
(
cacheMap
.
size
()
>=
capacity
);
}
@Override
public
int
size
()
{
return
cacheMap
.
size
();
}
@Override
public
boolean
isEmpty
()
{
return
cacheMap
.
isEmpty
();
}
@Override
public
String
toString
()
{
return
this
.
cacheMap
.
toString
();
}
// ---------------------------------------------------------------- common end
/**
* 设置监听
*
* @param listener 监听
* @return this
* @since 5.5.2
*/
@Override
public
AbstractCache
<
K
,
V
>
setListener
(
CacheListener
<
K
,
V
>
listener
)
{
this
.
listener
=
listener
;
return
this
;
}
/**
* 返回所有键
*
* @return 所有键
* @since 5.5.9
*/
public
Set
<
K
>
keySet
(){
return
this
.
cacheMap
.
keySet
().
stream
().
map
(
Mutable:
:
get
).
collect
(
Collectors
.
toSet
());
}
/**
* 对象移除回调。默认无动作<br>
* 子类可重写此方法用于监听移除事件,如果重写,listener将无效
*
* @param key 键
* @param cachedObject 被缓存的对象
*/
protected
void
onRemove
(
K
key
,
V
cachedObject
)
{
final
CacheListener
<
K
,
V
>
listener
=
this
.
listener
;
if
(
null
!=
listener
)
{
listener
.
onRemove
(
key
,
cachedObject
);
}
}
/**
* 移除key对应的对象,不加锁
*
* @param key 键
* @param withMissCount 是否计数丢失数
* @return 移除的对象,无返回null
*/
protected
CacheObj
<
K
,
V
>
removeWithoutLock
(
K
key
,
boolean
withMissCount
)
{
final
CacheObj
<
K
,
V
>
co
=
cacheMap
.
remove
(
MutableObj
.
of
(
key
));
if
(
withMissCount
)
{
// 在丢失计数有效的情况下,移除一般为get时的超时操作,此处应该丢失数+1
this
.
missCount
.
increment
();
}
return
co
;
}
/**
* 获取所有{@link CacheObj}值的{@link Iterator}形式
* @return {@link Iterator}
* @since 5.8.0
*/
protected
Iterator
<
CacheObj
<
K
,
V
>>
cacheObjIter
(){
return
this
.
cacheMap
.
values
().
iterator
();
}
}
hutool-cache/src/main/java/cn/hutool/cache/impl/CacheObj.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.impl
;
import
cn.hutool.core.date.DateUtil
;
import
java.io.Serializable
;
import
java.util.Date
;
import
java.util.concurrent.atomic.AtomicLong
;
/**
* 缓存对象
*
* @param <K> Key类型
* @param <V> Value类型
* @author Looly
*/
public
class
CacheObj
<
K
,
V
>
implements
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
protected
final
K
key
;
protected
final
V
obj
;
/**
* 上次访问时间
*/
protected
volatile
long
lastAccess
;
/**
* 访问次数
*/
protected
AtomicLong
accessCount
=
new
AtomicLong
();
/**
* 对象存活时长,0表示永久存活
*/
protected
final
long
ttl
;
/**
* 构造
*
* @param key 键
* @param obj 值
* @param ttl 超时时长
*/
protected
CacheObj
(
K
key
,
V
obj
,
long
ttl
)
{
this
.
key
=
key
;
this
.
obj
=
obj
;
this
.
ttl
=
ttl
;
this
.
lastAccess
=
System
.
currentTimeMillis
();
}
/**
* 获取键
*
* @return 键
* @since 4.0.10
*/
public
K
getKey
()
{
return
this
.
key
;
}
/**
* 获取值
*
* @return 值
* @since 4.0.10
*/
public
V
getValue
()
{
return
this
.
obj
;
}
/**
* 获取对象存活时长,即超时总时长,0表示无限
*
* @return 对象存活时长
* @since 5.7.17
*/
public
long
getTtl
()
{
return
this
.
ttl
;
}
/**
* 获取过期时间,返回{@code null}表示永不过期
*
* @return 此对象的过期时间,返回{@code null}表示永不过期
* @since 5.7.17
*/
public
Date
getExpiredTime
(){
if
(
this
.
ttl
>
0
){
return
DateUtil
.
date
(
this
.
lastAccess
+
this
.
ttl
);
}
return
null
;
}
/**
* 获取上次访问时间
*
* @return 上次访问时间
* @since 5.7.17
*/
public
long
getLastAccess
()
{
return
this
.
lastAccess
;
}
@Override
public
String
toString
()
{
return
"CacheObj [key="
+
key
+
", obj="
+
obj
+
", lastAccess="
+
lastAccess
+
", accessCount="
+
accessCount
+
", ttl="
+
ttl
+
"]"
;
}
/**
* 判断是否过期
*
* @return 是否过期
*/
protected
boolean
isExpired
()
{
if
(
this
.
ttl
>
0
)
{
// 此处不考虑时间回拨
return
(
System
.
currentTimeMillis
()
-
this
.
lastAccess
)
>
this
.
ttl
;
}
return
false
;
}
/**
* 获取值
*
* @param isUpdateLastAccess 是否更新最后访问时间
* @return 获得对象
* @since 4.0.10
*/
protected
V
get
(
boolean
isUpdateLastAccess
)
{
if
(
isUpdateLastAccess
)
{
lastAccess
=
System
.
currentTimeMillis
();
}
accessCount
.
getAndIncrement
();
return
this
.
obj
;
}
}
hutool-cache/src/main/java/cn/hutool/cache/impl/CacheObjIterator.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.impl
;
import
java.io.Serializable
;
import
java.util.Iterator
;
import
java.util.NoSuchElementException
;
/**
* {@link cn.hutool.cache.impl.AbstractCache} 的CacheObj迭代器.
*
* @author looly
*
* @param <K> 键类型
* @param <V> 值类型
* @since 4.0.10
*/
public
class
CacheObjIterator
<
K
,
V
>
implements
Iterator
<
CacheObj
<
K
,
V
>>,
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
private
final
Iterator
<
CacheObj
<
K
,
V
>>
iterator
;
private
CacheObj
<
K
,
V
>
nextValue
;
/**
* 构造
*
* @param iterator 原{@link Iterator}
*/
CacheObjIterator
(
Iterator
<
CacheObj
<
K
,
V
>>
iterator
)
{
this
.
iterator
=
iterator
;
nextValue
();
}
/**
* @return 是否有下一个值
*/
@Override
public
boolean
hasNext
()
{
return
nextValue
!=
null
;
}
/**
* @return 下一个值
*/
@Override
public
CacheObj
<
K
,
V
>
next
()
{
if
(
false
==
hasNext
())
{
throw
new
NoSuchElementException
();
}
final
CacheObj
<
K
,
V
>
cachedObject
=
nextValue
;
nextValue
();
return
cachedObject
;
}
/**
* 从缓存中移除没有过期的当前值,此方法不支持
*/
@Override
public
void
remove
()
{
throw
new
UnsupportedOperationException
(
"Cache values Iterator is not support to modify."
);
}
/**
* 下一个值,当不存在则下一个值为null
*/
private
void
nextValue
()
{
while
(
iterator
.
hasNext
())
{
nextValue
=
iterator
.
next
();
if
(
nextValue
.
isExpired
()
==
false
)
{
return
;
}
}
nextValue
=
null
;
}
}
hutool-cache/src/main/java/cn/hutool/cache/impl/CacheValuesIterator.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.impl
;
import
java.io.Serializable
;
import
java.util.Iterator
;
/**
* {@link cn.hutool.cache.impl.AbstractCache} 的值迭代器.
* @author looly
*
* @param <V> 迭代对象类型
*/
public
class
CacheValuesIterator
<
V
>
implements
Iterator
<
V
>,
Serializable
{
private
static
final
long
serialVersionUID
=
1L
;
private
final
CacheObjIterator
<?,
V
>
cacheObjIter
;
/**
* 构造
* @param iterator 原{@link CacheObjIterator}
*/
CacheValuesIterator
(
CacheObjIterator
<?,
V
>
iterator
)
{
this
.
cacheObjIter
=
iterator
;
}
/**
* @return 是否有下一个值
*/
@Override
public
boolean
hasNext
()
{
return
this
.
cacheObjIter
.
hasNext
();
}
/**
* @return 下一个值
*/
@Override
public
V
next
()
{
return
cacheObjIter
.
next
().
getValue
();
}
/**
* 从缓存中移除没有过期的当前值,不支持此方法
*/
@Override
public
void
remove
()
{
cacheObjIter
.
remove
();
}
}
hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.impl
;
import
java.util.Iterator
;
import
java.util.LinkedHashMap
;
/**
* FIFO(first in first out) 先进先出缓存.
*
* <p>
* 元素不停的加入缓存直到缓存满为止,当缓存满时,清理过期缓存对象,清理后依旧满则删除先入的缓存(链表首部对象)<br>
* 优点:简单快速 <br>
* 缺点:不灵活,不能保证最常用的对象总是被保留
* </p>
*
* @param <K> 键类型
* @param <V> 值类型
* @author Looly
*/
public
class
FIFOCache
<
K
,
V
>
extends
StampedCache
<
K
,
V
>
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 构造,默认对象不过期
*
* @param capacity 容量
*/
public
FIFOCache
(
int
capacity
)
{
this
(
capacity
,
0
);
}
/**
* 构造
*
* @param capacity 容量
* @param timeout 过期时长
*/
public
FIFOCache
(
int
capacity
,
long
timeout
)
{
this
.
capacity
=
capacity
;
this
.
timeout
=
timeout
;
cacheMap
=
new
LinkedHashMap
<>(
capacity
+
1
,
1.0f
,
false
);
}
/**
* 先进先出的清理策略<br>
* 先遍历缓存清理过期的缓存对象,如果清理后还是满的,则删除第一个缓存对象
*/
@Override
protected
int
pruneCache
()
{
int
count
=
0
;
CacheObj
<
K
,
V
>
first
=
null
;
// 清理过期对象并找出链表头部元素(先入元素)
final
Iterator
<
CacheObj
<
K
,
V
>>
values
=
cacheObjIter
();
if
(
isPruneExpiredActive
())
{
// 清理过期对象并找出链表头部元素(先入元素)
while
(
values
.
hasNext
())
{
CacheObj
<
K
,
V
>
co
=
values
.
next
();
if
(
co
.
isExpired
())
{
values
.
remove
();
onRemove
(
co
.
key
,
co
.
obj
);
count
++;
continue
;
}
if
(
first
==
null
)
{
first
=
co
;
}
}
}
else
{
first
=
values
.
hasNext
()
?
values
.
next
()
:
null
;
}
// 清理结束后依旧是满的,则删除第一个被缓存的对象
if
(
isFull
()
&&
null
!=
first
)
{
removeWithoutLock
(
first
.
key
,
false
);
onRemove
(
first
.
key
,
first
.
obj
);
count
++;
}
return
count
;
}
}
hutool-cache/src/main/java/cn/hutool/cache/impl/LFUCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.impl
;
import
java.util.HashMap
;
import
java.util.Iterator
;
/**
* LFU(least frequently used) 最少使用率缓存<br>
* 根据使用次数来判定对象是否被持续缓存<br>
* 使用率是通过访问次数计算的。<br>
* 当缓存满时清理过期对象。<br>
* 清理后依旧满的情况下清除最少访问(访问计数最小)的对象并将其他对象的访问数减去这个最小访问数,以便新对象进入后可以公平计数。
*
* @author Looly,jodd
*
* @param <K> 键类型
* @param <V> 值类型
*/
public
class
LFUCache
<
K
,
V
>
extends
StampedCache
<
K
,
V
>
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 构造
*
* @param capacity 容量
*/
public
LFUCache
(
int
capacity
)
{
this
(
capacity
,
0
);
}
/**
* 构造
*
* @param capacity 容量
* @param timeout 过期时长
*/
public
LFUCache
(
int
capacity
,
long
timeout
)
{
if
(
Integer
.
MAX_VALUE
==
capacity
)
{
capacity
-=
1
;
}
this
.
capacity
=
capacity
;
this
.
timeout
=
timeout
;
cacheMap
=
new
HashMap
<>(
capacity
+
1
,
1.0f
);
}
// ---------------------------------------------------------------- prune
/**
* 清理过期对象。<br>
* 清理后依旧满的情况下清除最少访问(访问计数最小)的对象并将其他对象的访问数减去这个最小访问数,以便新对象进入后可以公平计数。
*
* @return 清理个数
*/
@Override
protected
int
pruneCache
()
{
int
count
=
0
;
CacheObj
<
K
,
V
>
comin
=
null
;
// 清理过期对象并找出访问最少的对象
Iterator
<
CacheObj
<
K
,
V
>>
values
=
cacheObjIter
();
CacheObj
<
K
,
V
>
co
;
while
(
values
.
hasNext
())
{
co
=
values
.
next
();
if
(
co
.
isExpired
()
==
true
)
{
values
.
remove
();
onRemove
(
co
.
key
,
co
.
obj
);
count
++;
continue
;
}
//找出访问最少的对象
if
(
comin
==
null
||
co
.
accessCount
.
get
()
<
comin
.
accessCount
.
get
())
{
comin
=
co
;
}
}
// 减少所有对象访问量,并清除减少后为0的访问对象
if
(
isFull
()
&&
comin
!=
null
)
{
long
minAccessCount
=
comin
.
accessCount
.
get
();
values
=
cacheObjIter
();
CacheObj
<
K
,
V
>
co1
;
while
(
values
.
hasNext
())
{
co1
=
values
.
next
();
if
(
co1
.
accessCount
.
addAndGet
(-
minAccessCount
)
<=
0
)
{
values
.
remove
();
onRemove
(
co1
.
key
,
co1
.
obj
);
count
++;
}
}
}
return
count
;
}
}
hutool-cache/src/main/java/cn/hutool/cache/impl/LRUCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.impl
;
import
cn.hutool.core.lang.mutable.Mutable
;
import
cn.hutool.core.map.FixedLinkedHashMap
;
import
java.util.Iterator
;
/**
* LRU (least recently used)最近最久未使用缓存<br>
* 根据使用时间来判定对象是否被持续缓存<br>
* 当对象被访问时放入缓存,当缓存满了,最久未被使用的对象将被移除。<br>
* 此缓存基于LinkedHashMap,因此当被缓存的对象每被访问一次,这个对象的key就到链表头部。<br>
* 这个算法简单并且非常快,他比FIFO有一个显著优势是经常使用的对象不太可能被移除缓存。<br>
* 缺点是当缓存满时,不能被很快的访问。
* @author Looly,jodd
*
* @param <K> 键类型
* @param <V> 值类型
*/
public
class
LRUCache
<
K
,
V
>
extends
ReentrantCache
<
K
,
V
>
{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 构造<br>
* 默认无超时
* @param capacity 容量
*/
public
LRUCache
(
int
capacity
)
{
this
(
capacity
,
0
);
}
/**
* 构造
* @param capacity 容量
* @param timeout 默认超时时间,单位:毫秒
*/
public
LRUCache
(
int
capacity
,
long
timeout
)
{
if
(
Integer
.
MAX_VALUE
==
capacity
)
{
capacity
-=
1
;
}
this
.
capacity
=
capacity
;
this
.
timeout
=
timeout
;
//链表key按照访问顺序排序,调用get方法后,会将这次访问的元素移至头部
final
FixedLinkedHashMap
<
Mutable
<
K
>,
CacheObj
<
K
,
V
>>
fixedLinkedHashMap
=
new
FixedLinkedHashMap
<>(
capacity
);
fixedLinkedHashMap
.
setRemoveListener
(
entry
->
{
if
(
null
!=
listener
){
listener
.
onRemove
(
entry
.
getKey
().
get
(),
entry
.
getValue
().
getValue
());
}
});
cacheMap
=
fixedLinkedHashMap
;
}
// ---------------------------------------------------------------- prune
/**
* 只清理超时对象,LRU的实现会交给{@code LinkedHashMap}
*/
@Override
protected
int
pruneCache
()
{
if
(
isPruneExpiredActive
()
==
false
)
{
return
0
;
}
int
count
=
0
;
Iterator
<
CacheObj
<
K
,
V
>>
values
=
cacheObjIter
();
CacheObj
<
K
,
V
>
co
;
while
(
values
.
hasNext
())
{
co
=
values
.
next
();
if
(
co
.
isExpired
())
{
values
.
remove
();
onRemove
(
co
.
key
,
co
.
obj
);
count
++;
}
}
return
count
;
}
}
hutool-cache/src/main/java/cn/hutool/cache/impl/NoCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.impl
;
import
cn.hutool.cache.Cache
;
import
cn.hutool.core.lang.func.Func0
;
import
java.util.Iterator
;
/**
* 无缓存实现,用于快速关闭缓存
*
* @param <K> 键类型
* @param <V> 值类型
* @author Looly,jodd
*/
public
class
NoCache
<
K
,
V
>
implements
Cache
<
K
,
V
>
{
private
static
final
long
serialVersionUID
=
1L
;
@Override
public
int
capacity
()
{
return
0
;
}
@Override
public
long
timeout
()
{
return
0
;
}
@Override
public
void
put
(
K
key
,
V
object
)
{
// 跳过
}
@Override
public
void
put
(
K
key
,
V
object
,
long
timeout
)
{
// 跳过
}
@Override
public
boolean
containsKey
(
K
key
)
{
return
false
;
}
@Override
public
V
get
(
K
key
)
{
return
null
;
}
@Override
public
V
get
(
K
key
,
boolean
isUpdateLastAccess
)
{
return
null
;
}
@Override
public
V
get
(
K
key
,
Func0
<
V
>
supplier
)
{
return
get
(
key
,
true
,
supplier
);
}
@Override
public
V
get
(
K
key
,
boolean
isUpdateLastAccess
,
Func0
<
V
>
supplier
)
{
try
{
return
(
null
==
supplier
)
?
null
:
supplier
.
call
();
}
catch
(
Exception
e
)
{
throw
new
RuntimeException
(
e
);
}
}
@Override
public
Iterator
<
V
>
iterator
()
{
return
new
Iterator
<
V
>()
{
@Override
public
boolean
hasNext
()
{
return
false
;
}
@Override
public
V
next
()
{
return
null
;
}
};
}
@Override
public
Iterator
<
CacheObj
<
K
,
V
>>
cacheObjIterator
()
{
return
null
;
}
@Override
public
int
prune
()
{
return
0
;
}
@Override
public
boolean
isFull
()
{
return
false
;
}
@Override
public
void
remove
(
K
key
)
{
// 跳过
}
@Override
public
void
clear
()
{
// 跳过
}
@Override
public
int
size
()
{
return
0
;
}
@Override
public
boolean
isEmpty
()
{
return
false
;
}
}
hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.impl
;
import
cn.hutool.core.collection.CopiedIter
;
import
java.util.Iterator
;
import
java.util.concurrent.locks.ReentrantLock
;
/**
* 使用{@link ReentrantLock}保护的缓存,读写都使用悲观锁完成,主要避免某些Map无法使用读写锁的问题<br>
* 例如使用了LinkedHashMap的缓存,由于get方法也会改变Map的结构,因此读写必须加互斥锁
*
* @param <K> 键类型
* @param <V> 值类型
* @author looly
* @since 5.7.15
*/
public
abstract
class
ReentrantCache
<
K
,
V
>
extends
AbstractCache
<
K
,
V
>
{
private
static
final
long
serialVersionUID
=
1L
;
// 一些特殊缓存,例如使用了LinkedHashMap的缓存,由于get方法也会改变Map的结构,导致无法使用读写锁
// TODO 最优的解决方案是使用Guava的ConcurrentLinkedHashMap,此处使用简化的互斥锁
protected
final
ReentrantLock
lock
=
new
ReentrantLock
();
@Override
public
void
put
(
K
key
,
V
object
,
long
timeout
)
{
lock
.
lock
();
try
{
putWithoutLock
(
key
,
object
,
timeout
);
}
finally
{
lock
.
unlock
();
}
}
@Override
public
boolean
containsKey
(
K
key
)
{
lock
.
lock
();
try
{
// 不存在或已移除
final
CacheObj
<
K
,
V
>
co
=
getWithoutLock
(
key
);
if
(
co
==
null
)
{
return
false
;
}
if
(
false
==
co
.
isExpired
())
{
// 命中
return
true
;
}
}
finally
{
lock
.
unlock
();
}
// 过期
remove
(
key
,
true
);
return
false
;
}
@Override
public
V
get
(
K
key
,
boolean
isUpdateLastAccess
)
{
CacheObj
<
K
,
V
>
co
;
lock
.
lock
();
try
{
co
=
getWithoutLock
(
key
);
}
finally
{
lock
.
unlock
();
}
// 未命中
if
(
null
==
co
)
{
missCount
.
increment
();
return
null
;
}
else
if
(
false
==
co
.
isExpired
())
{
hitCount
.
increment
();
return
co
.
get
(
isUpdateLastAccess
);
}
// 过期,既不算命中也不算非命中
remove
(
key
,
true
);
return
null
;
}
@Override
public
Iterator
<
CacheObj
<
K
,
V
>>
cacheObjIterator
()
{
CopiedIter
<
CacheObj
<
K
,
V
>>
copiedIterator
;
lock
.
lock
();
try
{
copiedIterator
=
CopiedIter
.
copyOf
(
cacheObjIter
());
}
finally
{
lock
.
unlock
();
}
return
new
CacheObjIterator
<>(
copiedIterator
);
}
@Override
public
final
int
prune
()
{
lock
.
lock
();
try
{
return
pruneCache
();
}
finally
{
lock
.
unlock
();
}
}
@Override
public
void
remove
(
K
key
)
{
remove
(
key
,
false
);
}
@Override
public
void
clear
()
{
lock
.
lock
();
try
{
cacheMap
.
clear
();
}
finally
{
lock
.
unlock
();
}
}
@Override
public
String
toString
()
{
lock
.
lock
();
try
{
return
super
.
toString
();
}
finally
{
lock
.
unlock
();
}
}
/**
* 移除key对应的对象
*
* @param key 键
* @param withMissCount 是否计数丢失数
*/
private
void
remove
(
K
key
,
boolean
withMissCount
)
{
lock
.
lock
();
CacheObj
<
K
,
V
>
co
;
try
{
co
=
removeWithoutLock
(
key
,
withMissCount
);
}
finally
{
lock
.
unlock
();
}
if
(
null
!=
co
)
{
onRemove
(
co
.
key
,
co
.
obj
);
}
}
}
hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.impl
;
import
cn.hutool.core.collection.CopiedIter
;
import
java.util.Iterator
;
import
java.util.concurrent.locks.StampedLock
;
/**
* 使用{@link StampedLock}保护的缓存,使用读写乐观锁
*
* @param <K> 键类型
* @param <V> 值类型
* @author looly
* @since 5.7.15
*/
public
abstract
class
StampedCache
<
K
,
V
>
extends
AbstractCache
<
K
,
V
>{
private
static
final
long
serialVersionUID
=
1L
;
// 乐观锁,此处使用乐观锁解决读多写少的场景
// get时乐观读,再检查是否修改,修改则转入悲观读重新读一遍,可以有效解决在写时阻塞大量读操作的情况。
// see: https://www.cnblogs.com/jiagoushijuzi/p/13721319.html
protected
final
StampedLock
lock
=
new
StampedLock
();
@Override
public
void
put
(
K
key
,
V
object
,
long
timeout
)
{
final
long
stamp
=
lock
.
writeLock
();
try
{
putWithoutLock
(
key
,
object
,
timeout
);
}
finally
{
lock
.
unlockWrite
(
stamp
);
}
}
@Override
public
boolean
containsKey
(
K
key
)
{
final
long
stamp
=
lock
.
readLock
();
try
{
// 不存在或已移除
final
CacheObj
<
K
,
V
>
co
=
getWithoutLock
(
key
);
if
(
co
==
null
)
{
return
false
;
}
if
(
false
==
co
.
isExpired
())
{
// 命中
return
true
;
}
}
finally
{
lock
.
unlockRead
(
stamp
);
}
// 过期
remove
(
key
,
true
);
return
false
;
}
@Override
public
V
get
(
K
key
,
boolean
isUpdateLastAccess
)
{
// 尝试读取缓存,使用乐观读锁
long
stamp
=
lock
.
tryOptimisticRead
();
CacheObj
<
K
,
V
>
co
=
getWithoutLock
(
key
);
if
(
false
==
lock
.
validate
(
stamp
)){
// 有写线程修改了此对象,悲观读
stamp
=
lock
.
readLock
();
try
{
co
=
getWithoutLock
(
key
);
}
finally
{
lock
.
unlockRead
(
stamp
);
}
}
// 未命中
if
(
null
==
co
)
{
missCount
.
increment
();
return
null
;
}
else
if
(
false
==
co
.
isExpired
())
{
hitCount
.
increment
();
return
co
.
get
(
isUpdateLastAccess
);
}
// 过期,既不算命中也不算非命中
remove
(
key
,
true
);
return
null
;
}
@Override
public
Iterator
<
CacheObj
<
K
,
V
>>
cacheObjIterator
()
{
CopiedIter
<
CacheObj
<
K
,
V
>>
copiedIterator
;
final
long
stamp
=
lock
.
readLock
();
try
{
copiedIterator
=
CopiedIter
.
copyOf
(
cacheObjIter
());
}
finally
{
lock
.
unlockRead
(
stamp
);
}
return
new
CacheObjIterator
<>(
copiedIterator
);
}
@Override
public
final
int
prune
()
{
final
long
stamp
=
lock
.
writeLock
();
try
{
return
pruneCache
();
}
finally
{
lock
.
unlockWrite
(
stamp
);
}
}
@Override
public
void
remove
(
K
key
)
{
remove
(
key
,
false
);
}
@Override
public
void
clear
()
{
final
long
stamp
=
lock
.
writeLock
();
try
{
cacheMap
.
clear
();
}
finally
{
lock
.
unlockWrite
(
stamp
);
}
}
/**
* 移除key对应的对象
*
* @param key 键
* @param withMissCount 是否计数丢失数
*/
private
void
remove
(
K
key
,
boolean
withMissCount
)
{
final
long
stamp
=
lock
.
writeLock
();
CacheObj
<
K
,
V
>
co
;
try
{
co
=
removeWithoutLock
(
key
,
withMissCount
);
}
finally
{
lock
.
unlockWrite
(
stamp
);
}
if
(
null
!=
co
)
{
onRemove
(
co
.
key
,
co
.
obj
);
}
}
}
hutool-cache/src/main/java/cn/hutool/cache/impl/TimedCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.impl
;
import
cn.hutool.cache.GlobalPruneTimer
;
import
cn.hutool.core.lang.mutable.Mutable
;
import
java.util.HashMap
;
import
java.util.Iterator
;
import
java.util.Map
;
import
java.util.concurrent.ScheduledFuture
;
/**
* 定时缓存<br>
* 此缓存没有容量限制,对象只有在过期后才会被移除
*
* @author Looly
*
* @param <K> 键类型
* @param <V> 值类型
*/
public
class
TimedCache
<
K
,
V
>
extends
StampedCache
<
K
,
V
>
{
private
static
final
long
serialVersionUID
=
1L
;
/** 正在执行的定时任务 */
private
ScheduledFuture
<?>
pruneJobFuture
;
/**
* 构造
*
* @param timeout 超时(过期)时长,单位毫秒
*/
public
TimedCache
(
long
timeout
)
{
this
(
timeout
,
new
HashMap
<>());
}
/**
* 构造
*
* @param timeout 过期时长
* @param map 存储缓存对象的map
*/
public
TimedCache
(
long
timeout
,
Map
<
Mutable
<
K
>,
CacheObj
<
K
,
V
>>
map
)
{
this
.
capacity
=
0
;
this
.
timeout
=
timeout
;
this
.
cacheMap
=
map
;
}
// ---------------------------------------------------------------- prune
/**
* 清理过期对象
*
* @return 清理数
*/
@Override
protected
int
pruneCache
()
{
int
count
=
0
;
final
Iterator
<
CacheObj
<
K
,
V
>>
values
=
cacheObjIter
();
CacheObj
<
K
,
V
>
co
;
while
(
values
.
hasNext
())
{
co
=
values
.
next
();
if
(
co
.
isExpired
())
{
values
.
remove
();
onRemove
(
co
.
key
,
co
.
obj
);
count
++;
}
}
return
count
;
}
// ---------------------------------------------------------------- auto prune
/**
* 定时清理
*
* @param delay 间隔时长,单位毫秒
*/
public
void
schedulePrune
(
long
delay
)
{
this
.
pruneJobFuture
=
GlobalPruneTimer
.
INSTANCE
.
schedule
(
this
::
prune
,
delay
);
}
/**
* 取消定时清理
*/
public
void
cancelPruneSchedule
()
{
if
(
null
!=
pruneJobFuture
)
{
pruneJobFuture
.
cancel
(
true
);
}
}
}
hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache.impl
;
import
cn.hutool.cache.CacheListener
;
import
cn.hutool.core.lang.Opt
;
import
cn.hutool.core.lang.mutable.Mutable
;
import
cn.hutool.core.map.WeakConcurrentMap
;
import
java.lang.ref.Reference
;
/**
* 弱引用缓存<br>
* 对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。<br>
* 丢弃某个键时,其条目从映射中有效地移除。<br>
*
* @author Looly
*
* @param <K> 键
* @param <V> 值
* @author looly
* @since 3.0.7
*/
public
class
WeakCache
<
K
,
V
>
extends
TimedCache
<
K
,
V
>{
private
static
final
long
serialVersionUID
=
1L
;
/**
* 构造
* @param timeout 超时时常,单位毫秒,-1或0表示无限制
*/
public
WeakCache
(
long
timeout
)
{
super
(
timeout
,
new
WeakConcurrentMap
<>());
}
@Override
public
WeakCache
<
K
,
V
>
setListener
(
CacheListener
<
K
,
V
>
listener
)
{
super
.
setListener
(
listener
);
final
WeakConcurrentMap
<
Mutable
<
K
>,
CacheObj
<
K
,
V
>>
map
=
(
WeakConcurrentMap
<
Mutable
<
K
>,
CacheObj
<
K
,
V
>>)
this
.
cacheMap
;
// WeakKey回收之后,key对应的值已经是null了,因此此处的key也为null
map
.
setPurgeListener
((
key
,
value
)->
listener
.
onRemove
(
Opt
.
ofNullable
(
key
).
map
(
Reference:
:
get
).
map
(
Mutable:
:
get
).
get
(),
value
.
getValue
()));
return
this
;
}
}
hutool-cache/src/main/java/cn/hutool/cache/impl/package-info.java
0 → 100644
View file @
45cda665
/**
* 提供各种缓存实现
*
* @author looly
*
*/
package
cn.hutool.cache.impl
;
\ No newline at end of file
hutool-cache/src/main/java/cn/hutool/cache/package-info.java
0 → 100644
View file @
45cda665
/**
* 提供简易的缓存实现,此模块参考了jodd工具中的Cache模块
*
* @author looly
*
*/
package
cn.hutool.cache
;
\ No newline at end of file
hutool-cache/src/test/java/cn/hutool/cache/CacheConcurrentTest.java
0 → 100644
View file @
45cda665
package
cn.hutool.cache
;
import
cn.hutool.cache.impl.FIFOCache
;
import
cn.hutool.cache.impl.LRUCache
;
import
cn.hutool.cache.impl.WeakCache
;
import
cn.hutool.core.lang.Console
;
import
cn.hutool.core.thread.ConcurrencyTester
;
import
cn.hutool.core.thread.ThreadUtil
;
import
org.junit.Assert
;
import
org.junit.Ignore
;
import
org.junit.Test
;
import
java.util.concurrent.atomic.AtomicInteger
;
/**
* 缓存单元测试
*
* @author looly
*
*/
public
class
CacheConcurrentTest
{
@Test
@Ignore
public
void
fifoCacheTest
()
{
int
threadCount
=
4000
;
final
Cache
<
String
,
String
>
cache
=
new
FIFOCache
<>(
3
);
// 由于缓存容量只有3,当加入第四个元素的时候,根据FIFO规则,最先放入的对象将被移除
for
(
int
i
=
0
;
i
<
threadCount
;
i
++)
{
ThreadUtil
.
execute
(()
->
{
cache
.
put
(
"key1"
,
"value1"
,
System
.
currentTimeMillis
()
*
3
);
cache
.
put
(
"key2"
,
"value2"
,
System
.
currentTimeMillis
()
*
3
);
cache
.
put
(
"key3"
,
"value3"
,
System
.
currentTimeMillis
()
*
3
);
cache
.
put
(
"key4"
,
"value4"
,
System
.
currentTimeMillis
()
*
3
);
ThreadUtil
.
sleep
(
1000
);
cache
.
put
(
"key5"
,
"value5"
,
System
.
currentTimeMillis
()
*
3
);
cache
.
put
(
"key6"
,
"value6"
,
System
.
currentTimeMillis
()
*
3
);
cache
.
put
(
"key7"
,
"value7"
,
System
.
currentTimeMillis
()
*
3
);
cache
.
put
(
"key8"
,
"value8"
,
System
.
currentTimeMillis
()
*
3
);
Console
.
log
(
"put all"
);
});
}
for
(
int
i
=
0
;
i
<
threadCount
;
i
++)
{
ThreadUtil
.
execute
(()
->
show
(
cache
));
}
System
.
out
.
println
(
"=============================="
);
ThreadUtil
.
sleep
(
10000
);
}
@Test
@Ignore
public
void
lruCacheTest
()
{
int
threadCount
=
40000
;
final
Cache
<
String
,
String
>
cache
=
new
LRUCache
<>(
1000
);
for
(
int
i
=
0
;
i
<
threadCount
;
i
++)
{
final
int
index
=
i
;
ThreadUtil
.
execute
(()
->
{
cache
.
put
(
"key1"
+
index
,
"value1"
);
cache
.
put
(
"key2"
+
index
,
"value2"
,
System
.
currentTimeMillis
()
*
3
);
int
size
=
cache
.
size
();
int
capacity
=
cache
.
capacity
();
if
(
size
>
capacity
)
{
Console
.
log
(
"{} {}"
,
size
,
capacity
);
}
ThreadUtil
.
sleep
(
1000
);
size
=
cache
.
size
();
capacity
=
cache
.
capacity
();
if
(
size
>
capacity
)
{
Console
.
log
(
"## {} {}"
,
size
,
capacity
);
}
});
}
ThreadUtil
.
sleep
(
5000
);
}
private
void
show
(
Cache
<
String
,
String
>
cache
)
{
for
(
Object
tt
:
cache
)
{
Console
.
log
(
tt
);
}
}
@Test
public
void
effectiveTest
()
{
// 模拟耗时操作消耗时间
int
delay
=
2000
;
AtomicInteger
ai
=
new
AtomicInteger
(
0
);
WeakCache
<
Integer
,
Integer
>
weakCache
=
new
WeakCache
<>(
60
*
1000
);
ConcurrencyTester
concurrencyTester
=
ThreadUtil
.
concurrencyTest
(
32
,
()
->
{
int
i
=
ai
.
incrementAndGet
()
%
4
;
weakCache
.
get
(
i
,
()
->
{
ThreadUtil
.
sleep
(
delay
);
return
i
;
});
});
long
interval
=
concurrencyTester
.
getInterval
();
// 总耗时应与单次操作耗时在同一个数量级
Assert
.
assertTrue
(
interval
<
delay
*
2
);
}
}
Prev
1
2
3
4
5
6
7
8
9
…
19
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment