public interface Cache extends Closeable {
Logger logger = LoggerFactory.getLogger(Cache.class);
default V get(K key) throws CacheInvokeException {
CacheGetResult result = GET(key);
if (result.isSuccess()) {
return result.getValue();
} else {
return null;
}
}
default Map getAll(Set extends K> keys) throws CacheInvokeException {
MultiGetResult cacheGetResults = GET_ALL(keys);
return cacheGetResults.unwrapValues();
}
default void put(K key, V value) {
PUT(key, value);
}
default void putAll(Map extends K, ? extends V> map) {
PUT_ALL(map);
}
default boolean putIfAbsent(K key, V value) {
CacheResult result = PUT_IF_ABSENT(key, value, config().getExpireAfterWriteInMillis(), TimeUnit.MILLISECONDS);
return result.getResultCode() == CacheResultCode.SUCCESS;
}
default boolean remove(K key) {
return REMOVE(key).isSuccess();
}
default void removeAll(Set extends K> keys) {
REMOVE_ALL(keys);
}
T unwrap(Class clazz);
@Override
default void close() {
}
CacheConfig config();
default AutoReleaseLock tryLock(K key, long expire, TimeUnit timeUnit) {
if (key == null) {
return null;
}
final String uuid = UUID.randomUUID().toString();
final long expireTimestamp = System.currentTimeMillis() + timeUnit.toMillis(expire);
final CacheConfig config = config();
AutoReleaseLock lock = () -> {
int unlockCount = 0;
while (unlockCount++ < config.getTryLockUnlockCount()) {
if(System.currentTimeMillis() < expireTimestamp) {
CacheResult unlockResult = REMOVE(key);
if (unlockResult.getResultCode() == CacheResultCode.FAIL
|| unlockResult.getResultCode() == CacheResultCode.PART_SUCCESS) {
logger.info("[tryLock] [{} of {}] [{}] unlock failed. Key={}, msg = {}",
unlockCount, config.getTryLockUnlockCount(), uuid, key, unlockResult.getMessage());
// retry
} else if (unlockResult.isSuccess()) {
logger.debug("[tryLock] [{} of {}] [{}] successfully release the lock. Key={}",
unlockCount, config.getTryLockUnlockCount(), uuid, key);
return;
} else {
logger.warn("[tryLock] [{} of {}] [{}] unexpected unlock result: Key={}, result={}",
unlockCount, config.getTryLockUnlockCount(), uuid, key, unlockResult.getResultCode());
return;
}
} else {
logger.info("[tryLock] [{} of {}] [{}] lock already expired: Key={}",
unlockCount, config.getTryLockUnlockCount(), uuid, key);
return;
}
}
};
int lockCount = 0;
Cache cache = this;
while (lockCount++ < config.getTryLockLockCount()) {
CacheResult lockResult = cache.PUT_IF_ABSENT(key, uuid, expire, timeUnit);
if (lockResult.isSuccess()) {
logger.debug("[tryLock] [{} of {}] [{}] successfully get a lock. Key={}",
lockCount, config.getTryLockLockCount(), uuid, key);
return lock;
} else if (lockResult.getResultCode() == CacheResultCode.FAIL || lockResult.getResultCode() == CacheResultCode.PART_SUCCESS) {
logger.info("[tryLock] [{} of {}] [{}] cache access failed during get lock, will inquiry {} times. Key={}, msg={}",
lockCount, config.getTryLockLockCount(), uuid,
config.getTryLockInquiryCount(), key, lockResult.getMessage());
int inquiryCount = 0;
while (inquiryCount++ < config.getTryLockInquiryCount()) {
CacheGetResult inquiryResult = cache.GET(key);
if (inquiryResult.isSuccess()) {
if (uuid.equals(inquiryResult.getValue())) {
logger.debug("[tryLock] [{} of {}] [{}] successfully get a lock after inquiry. Key={}",
inquiryCount, config.getTryLockInquiryCount(), uuid, key);
return lock;
} else {
logger.debug("[tryLock] [{} of {}] [{}] not the owner of the lock, return null. Key={}",
inquiryCount, config.getTryLockInquiryCount(), uuid, key);
return null;
}
} else {
logger.info("[tryLock] [{} of {}] [{}] inquiry failed. Key={}, msg={}",
inquiryCount, config.getTryLockInquiryCount(), uuid, key, inquiryResult.getMessage());
// retry inquiry
}
}
} else {
// others holds the lock
logger.debug("[tryLock] [{} of {}] [{}] others holds the lock, return null. Key={}",
lockCount, config.getTryLockLockCount(), uuid, key);
return null;
}
}
logger.debug("[tryLock] [{}] return null after {} attempts. Key={}", uuid, config.getTryLockLockCount(), key);
return null;
}
default boolean tryLockAndRun(K key, long expire, TimeUnit timeUnit, Runnable action){
try (AutoReleaseLock lock = tryLock(key, expire, timeUnit)) {
if (lock != null) {
action.run();
return true;
} else {
return false;
}
}
}
CacheGetResult GET(K key);
MultiGetResult GET_ALL(Set extends K> keys);
default V computeIfAbsent(K key, Function loader) {
return computeIfAbsent(key, loader, config().isCacheNullValue());
}
V computeIfAbsent(K key, Function loader, boolean cacheNullWhenLoaderReturnNull);
V computeIfAbsent(K key, Function loader, boolean cacheNullWhenLoaderReturnNull, long expireAfterWrite, TimeUnit timeUnit);
default void put(K key, V value, long expireAfterWrite, TimeUnit timeUnit) {
PUT(key, value, expireAfterWrite, timeUnit);
}
default CacheResult PUT(K key, V value) {
if (key == null) {
return CacheResult.FAIL_ILLEGAL_ARGUMENT;
}
return PUT(key, value, config().getExpireAfterWriteInMillis(), TimeUnit.MILLISECONDS);
}
CacheResult PUT(K key, V value, long expireAfterWrite, TimeUnit timeUnit);
default void putAll(Map extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit) {
PUT_ALL(map, expireAfterWrite, timeUnit);
}
default CacheResult PUT_ALL(Map extends K, ? extends V> map) {
if (map == null) {
return CacheResult.FAIL_ILLEGAL_ARGUMENT;
}
return PUT_ALL(map, config().getExpireAfterWriteInMillis(), TimeUnit.MILLISECONDS);
}
CacheResult PUT_ALL(Map extends K, ? extends V> map, long expireAfterWrite, TimeUnit timeUnit);
CacheResult REMOVE(K key);
CacheResult REMOVE_ALL(Set extends K> keys);
CacheResult PUT_IF_ABSENT(K key, V value, long expireAfterWrite, TimeUnit timeUnit);
}
Cache接口主要是定义了大写的GET、GET_ALL、PUT、PUT_ALL、REMOVE、REMOVE_ALL、PUT_IF_ABSENT方法,以及基于这些大写方法包装的小写default方法
V get(K key)这样的方法虽然用起来方便,但有功能上的缺陷,当get返回null的时候,无法断定是对应的key不存在,还是访问缓存发生了异常,所以JetCache针对部分操作提供了另外一套API,提供了完整的返回值
另外主要是提供了tryLock、tryLockAndRun的default实现
public class OcuppyMoreThanHalf {
/**
* Q74 数组中有一个数字出现的次数超过了数组长度的一半,找出这个数字
* two solutions:
* 1.O(n)
* see <beauty of coding>--每次删除两个不同的数字,不改变数组的特性
* 2.O(nlogn)
* 排序。中间
cygwin很多命令显示command not found的解决办法
修改cygwin.BAT文件如下
@echo off
D:
set CYGWIN=tty notitle glob
set PATH=%PATH%;d:\cygwin\bin;d:\cygwin\sbin;d:\cygwin\usr\bin;d:\cygwin\usr\sbin;d:\cygwin\us