定时加载的单例缓存

import com.google.common.base.Preconditions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.Nonnull;
import javax.annotation.concurrent.ThreadSafe;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.function.Supplier;

/**
 * Created by zhaoyy on 2017/7/17.
 */
@ThreadSafe
public final class SingletonCache<T extends Serializable> implements Supplier<T> {

    private static final AtomicIntegerFieldUpdater<SingletonCache> STATE_UPDATER =
            AtomicIntegerFieldUpdater.newUpdater(SingletonCache.class, "state");
    private static final AtomicIntegerFieldUpdater<SingletonCache> RETRY_UPDATER =
            AtomicIntegerFieldUpdater.newUpdater(SingletonCache.class, "retry");

    private static final Logger logger = LoggerFactory.getLogger(SingletonCache.class);

    private static final int NOT_LOADING = 0;
    private static final int LOADING = 1;

    //null is NOT permitted
    private final SingletonLoader<T> loader;
    private final long expiredTimeMills;
    private final int maxRetry;
    private volatile int state = NOT_LOADING;
    private volatile int retry = 0;
    private volatile T value = null;
    private volatile long lastUpdateTime = -1;

    private SingletonCache(SingletonLoader<T> loader, long expiredTimeMills, T defaultValue, int maxRetry) {
        this.loader = loader;
        this.expiredTimeMills = expiredTimeMills;
        this.value = defaultValue;
        this.maxRetry = maxRetry;
    }


    public static <T extends Serializable> Builder<T> builder() {
        return new Builder<>();
    }


    @Nonnull
    @Override
    public T get() {
        if (value == null) {
            synchronized (this) {
                if (value == null) {
                    state = LOADING;
                    updateValueSync();
                }
            }
        } else {
            if ((System.currentTimeMillis() - lastUpdateTime > expiredTimeMills)
                    && STATE_UPDATER.compareAndSet(this, NOT_LOADING, LOADING)) {
                updateValueAsync();
            }

        }
        return value;
    }

    private void updateValueSync() {
        final T newValue;
        try {
            newValue = loader.load(value);
        } catch (Exception e) {
            throw new IllegalArgumentException(e);
        }
        if (newValue == null)
            throw new NullPointerException("null value is not permitted!");
        updateValueAndReleaseLock(newValue);
    }


    private void updateValueAndReleaseLock(T value) {
        this.value = value;
        lastUpdateTime = System.currentTimeMillis();
        retry = 0;
        state = NOT_LOADING;
    }

    private void updateValueAsync() {
        Thread thread = new Thread(this::loadValueRejectNull, "singleton-cache-async-runner");
        thread.start();
    }

    private void loadValueRejectNull() {
        final T newValue;
        try {
            newValue = loader.load(this.value);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            updateRetryUntilMaxRetry();
            return;
        }
        if (newValue == null) {
            updateRetryUntilMaxRetry();
            return;
        }
        updateValueAndReleaseLock(newValue);
    }

    private void updateRetryUntilMaxRetry() {
        assert state == LOADING;
        if (RETRY_UPDATER.getAndAdd(this, 1) < maxRetry) {
            state = NOT_LOADING;
            logger.warn("current retry:{}", retry);
            return;
        }
        logger.warn("retry over max times:{}, further update will not execute.", maxRetry);
    }


    @SuppressWarnings("WeakerAccess")
    public static final class Builder<T extends Serializable> {
        private long expiredTimeMills = -1;
        private T defaultValue = null;
        private SingletonLoader<T> loader = null;
        private int maxRetry = 5;

        public Builder<T> expiredAfter(long duration, TimeUnit unit) {
            Preconditions.checkArgument(duration >= 0 && unit != null);
            this.expiredTimeMills = unit.toMillis(duration);
            return this;
        }

        public Builder<T> defaultValue(@Nonnull T defaultValue) {
            this.defaultValue = defaultValue;
            return this;
        }

        public Builder<T> loader(@Nonnull SingletonLoader<T> loader) {
            this.loader = loader;
            return this;
        }

        public Builder<T> maxRetry(int maxRetry) {
            this.maxRetry = maxRetry;
            return this;
        }

        public SingletonCache<T> build() {
            Preconditions.checkArgument(expiredTimeMills >= 0, "expiredTimeMills < 0!");
            Preconditions.checkArgument(loader != null, "null loader!");
            Preconditions.checkArgument(maxRetry > 0, "max retry must be a positive number.");
            return new SingletonCache<>(loader, expiredTimeMills, defaultValue, maxRetry);
        }
    }
}
import javax.annotation.Nullable;
import java.io.Serializable;

/**
 * Created by zhaoyy on 2017/10/27.
 */
@FunctionalInterface
public interface SingletonLoader<T extends Serializable> {

    //T is expected as immutable or effectively immutable
    T load(@Nullable T oldValue) throws Exception;
}

 

转载于:https://my.oschina.net/u/2541538/blog/1456795

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值