简体   繁体   中英

How can I timeout the value of a variable to null when it's not updated in a specific interval of time in Java?

I have a class with set and get methods like the following:

public class MyClass {
    private Double variable = null;

    // set method
    public void setVariable(Double value){
        this.variable = value;
    }

    // get method
    public Double getVariable(){
        return this.variable;
    }
}

I'd like to make my set method in a way that it goes back to null if I don't update its value after a timeframe since it was settled. For example, let's say I create an instance of MyClass and use the method setVarible(2.2) inside it, if I don't update its value using the set method again inside 1 second I want it to go back to null . Is there any standard way of solving a problem like this in Java? What approach should I use to solve it?


Context:

I've thought of solving this problem declaring a global variable with a counter and always resetting the counter when the set method is run, but I got stuck because it would require asynchronous calls or threads for not stopping the main thread. Besides that, I didn't manage to make it work this way too and it seems like an overcomplicated solution to follow for a simple problem. I think the approach I'm trying to use is the wrong one to solve it.

You don't actually need to set the field to null actively, as long as it looks like you did on the outside (assuming of course that all access to the field goes through the appropriate getter):

public class MyClass {
    private static final long VARIABLE_FRESHNESS_THRESHOLD = 1 * 1000l;

    private Double variable = null;
    private long variableUpdateMillis = Long.MAX_VALUE;

    public void setVariable(Double value){
        this.variable = value;
        this.variableUpdateMillis = System.currentTimeMillis();
    }

    public Double getVariable(){
        if (System.currentTimeMillis() - this.variableUpdateMillis >= VARIABLE_FRESHNESS_THRESHOLD) {
          return null;
        }
        return this.variable;
    }
}

You can optimize that to avoid the currentTimeMillis() call if the last one already timed out (by setting the variable to null in the return null case and checking that first):

    public Double getVariable(){
        if (this.variable != null &&
            System.currentTimeMillis() - this.variableUpdateMillis >= VARIABLE_FRESHNESS_THRESHOLD) {
          this.variable = null;
        }
        return this.variable;
    }

As I suggested in the comments, I would start by setting an updateTime field with the current time stamp; and I would null the variable in get if the value wasn't updated within the correct time frame. I would also make the class generic (limiting it to Double seems unwise to me). Further, I would take advantage of Optional to indicate that this class returns an optional value. Putting that all together, it might look something like

public class MyClass<T> {
    private T variable = null;

    private long updateTime = System.currentTimeMillis();

    // set method
    public void setVariable(T value) {
        this.variable = value;
        updateTime = System.currentTimeMillis();
    }

    // get method
    public Optional<T> getVariable() {
        if (variable != null && System.currentTimeMillis() - updateTime <= 1000) {
            return Optional.of(variable);
        }
        variable = null;
        return Optional.empty();
    }
}

What I would propose is the make the object immutable and only re-assign the object to a new state once value has changed. Similar to Guavas stopwatch, you can only get the internal state if its within a threshold. You have to do an initial check or catch the exception if it is not. This does not allow for null values and it is immutable, and the object is effectively thread-safe. It also allows you to provide a getter regardless of state if needed in the future.

You could make getIfInThreshold() throw IllegalStateException but it's not really necessary if you access it properly using isInThreshold().

   public static class MyClass {

        private final Double variable;

        private final long lastAccessNanoseconds;

        private final TimeUnit thresholdUnit;

        private final long threshold;

        public MyClass(Double variable, long lastAccessNanoseconds, TimeUnit thresholdUnit, long threshold) {
            this.variable = Objects.requireNonNull(variable);
            this.lastAccessNanoseconds = lastAccessNanoseconds;
            this.thresholdUnit = thresholdUnit;
            this.threshold = threshold;
        }

        public MyClass(Double variable, TimeUnit thresholdUnit, long threshold) {
            this(variable, System.nanoTime(), thresholdUnit, threshold);
        }

        public static MyClass forSeconds(Double variable, long seconds) {
            return new MyClass(variable, TimeUnit.SECONDS, seconds);
        }

        public boolean isInThreshold() {
            return System.nanoTime() - lastAccessNanoseconds < TimeUnit.NANOSECONDS.convert(threshold, thresholdUnit);
        }

        public Double getIfInThreshold() {
            if (!isInThreshold()) {
                throw new IllegalStateException("Could not get variable, not within time threshold.");
            }
            return variable;
        }
    }

Example usage

    public static void main(String[] args) throws InterruptedException {
        MyClass myClass = MyClass.forSeconds(1.0, 1);

        Thread.sleep(2_000L);

        assert !myClass.isInThreshold();

        try {
            double value = myClass.getIfInThreshold();

            // cannot use value because of IllegalStateException
        } catch (IllegalStateException e) {
            System.out.println("Not accessible anymore!");
        }

        myClass = MyClass.forSeconds(.5, 1);

        Thread.sleep(500);

        double value = myClass.getIfInThreshold();
    }

A very simple approach, basically store the last set and validate in the get method

import java.util.Calendar;

public class TimeOutValue {

    private int TIME_OUT = 1000;

    private long lastSet = 0;

    private Double variable = null;

    // set method
    public void setVariable(Double value){
        lastSet = Calendar.getInstance().getTimeInMillis();
        this.variable = value;
    }

    // get method
    public Double getVariable(){
        if(lastSet != 0l && Calendar.getInstance().getTimeInMillis() - lastSet > TIME_OUT){
            this.variable = null;
            lastSet = 0l;
        }
        return this.variable;
    }


    public static void main(String args[]){
        TimeOutValue test = new TimeOutValue();

        test.setVariable(1.1);
        System.out.println(test.getVariable());
        try {
            Thread.sleep(500);
            System.out.println(test.getVariable());
            Thread.sleep(500);
            System.out.println(test.getVariable());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

}

The output for the test in main method

1.1
1.1
null

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM