1

I have a series of steps that does some operation (a sequence-of-steps) on two class variables (say var1 and var2). This operation has been schedules to run every 250 milliseconds with the help of a ScheduledExecutorService. What I want to do is that whenever I try to refer to the values in var1 and var2 from a separate thread, their state should confer with the atomicity of the sequence-of-steps that I have performed on them. So, let say I have the following code:

mySchedulesExecutor.scheduleAtFixedRate(new Runnable() {
........................
// these are my 'sequence-of-steps'
var1 += 1;
var1 %= 4;
var2 += 25;
........................
}, 0, 250, TimeUnit.MILLISECONDS);

Whenever I want to read the values of var1 and var2 from anywhere else, they should be consistent with the atomicity of the above mentioned sequence-of-steps. What is the best way to achieve this?

Swapnil
  • 1,870
  • 2
  • 23
  • 48
  • 4
    Use an explicit lock? – Mena Jul 13 '16 at 13:27
  • https://docs.oracle.com/javase/tutorial/essential/concurrency/locksync.html – Jiri Tousek Jul 13 '16 at 13:28
  • explicit lock and volatile variables – Mr_Thorynque Jul 13 '16 at 13:31
  • @JiriTousek : I have read through all the documentation of the essential concurrency that you mentioned. But, I am sill not sure if I use `synchronized(lock) {...}` around a set of operation, does it ensure me atomicity (as suggested by Mena above)? – Swapnil Jul 13 '16 at 13:31
  • It ensures `mutual exclusion` with all other threads that synchronize on the same object. In case all flows that read/write to these variables are synchronized on the same object, then yes, it can be considered atomic. – Yoav Gur Jul 13 '16 at 13:33
  • `synchronized (lock)` guarantees you that whoever wishes to enter (another or same) block synchronized on `lock` object will have to wait for any thread already in such block to exit it first. You need to secure *all* blocks that access your two variables using the `synchronized` block. A good practice is to use a dedicated `Object` instance as the lock object. It does **not** guarantee atomicity but it does guarantee isolation (inconsistent state won't be exposed to other threads). – Jiri Tousek Jul 13 '16 at 13:35
  • Thanks @JiriTousek , that was the comment that I needed. I guess this question quite naive in some sense. :) – Swapnil Jul 13 '16 at 13:37
  • Also note that atomicity and consistency are two different things - I edited my previous comment to highlight this. – Jiri Tousek Jul 13 '16 at 13:39
  • What do you mean when you say inconsistent here? – Swapnil Jul 13 '16 at 13:40
  • 1
    Inconsistent == one variable has changed but the other hasn't. This won't be seen by other threads while the writing thread is still in the synchronized block, and as long as the writing thread exits normally, it won't ever be seen by any reader. But if the thread encounters an exception and leaves the vars in inconsistent state, this state will remain in effect after leaving the synchronized block and may be later seen by the reader thread. – Jiri Tousek Jul 13 '16 at 14:44
  • https://stackoverflow.com/a/16906229/715269 - here is a conceptually good solution. (beware, the code is not correct) – Gangnus May 05 '19 at 12:09

4 Answers4

1

I believe that best practise for your situation is to use immutable object, where you store actual values of var1 and var2. Example:

public class Holder {
   private final double var1;
   private final double var2;

//constructor, getters ommitted
}

public class OuterClass {

  private volatile Holder holder = new Holder(0, 0);

  private void calculateNew() {

    //new calculation omitted
    holder = new Holder(newVar1, newVar2);
  }

  public Holder getVars() {
    return holder;
  }
}

With this solution, you dont need to use any ugly synchronized for consistency, so its guaranteed, that client from outside will always get consistence values of var1 a var2.

I believe that this solution is better than using synchronized, because with synchronized, you must use the same lock not just for writing variables but also for reading. So when you are writing new values, no other thread can read original values. As i understood from your original post, this is not the behaviour that you want. You just want the other threads to be able to read values continuously, even the old values, but always consistent values. Thats way its better to use immutable idiom because it will give you better response (other threads dont have to wait every time new value is writing)

lachty
  • 220
  • 2
  • 6
  • @latchy: Okay.. what are your views on an `synchronized` block being atomic (like suggested by some in the comments on the question)? – Swapnil Jul 13 '16 at 14:08
  • I also corrected one mistake - you have to return whole holder object, not just delegate the getters. – lachty Jul 13 '16 at 14:19
  • 1
    thanks for the answer. Makes sense what you suggested. Also, if you can clarify if the `synchronized` block ensures atomicity or not... it would be great. Thanks again! – Swapnil Jul 13 '16 at 14:20
  • Synchronized block means, that only one thread can access this block in the same time. Nothing more and nothing less. You as developer must ensure the atomicity and consistency with the options that Java offers. This can be done by synchronizing writing and accessing values with the same lock or you can use solution i have suggested, whis is better if I understood your scenario correctly. – lachty Jul 13 '16 at 14:30
  • Okay.. I will be very specific with my question now. What I mean to ask is if a thread once gets the lock on the `synchronized` block, is it ensured that it will complete all the steps inside this block before it gives access for the lock to another waiting thread on the same lock (but maybe a different block)? – Swapnil Jul 13 '16 at 14:39
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/117227/discussion-between-swapnil-and-lachty). – Swapnil Jul 13 '16 at 14:43
  • I like this solution but let's stress out that some care is still needed when using this if it is to produce "atomic" results - specifically, the user must make sure they're always reading both vars from the same holder, e.g. no `foo(getHolder().getVar1(), getHolder().getVar2())` – Jiri Tousek Jul 13 '16 at 14:48
  • Well for that what you can do it just get the reference of the holder once and then use it for accessing the values. So, if the values change after that, the one we have would be consistent. – Swapnil Jul 13 '16 at 21:19
  • It is a very strange post. Immutability should base on Atomicity (at creation), but not vice versa. – Gangnus May 05 '19 at 12:07
0

Then they should not be accessible as class (that is static, maybe volatile) variables.

One might think of transactions with commit/rollback. But I do not see a need here.

In fact one would need an object with fields var1 and var2 one may get and set atomically. Then one would always have a point-in-time guarantee.

That object might be immutable, any change resulting in a new object, like BigDecimal.

public class Data {
    public final int var1;
    public final int var2;
    public Data(int var1, int var2) {
        this.var1 = var1;
        this.var2 = var2;
    }
}

For many readers, one writer on time tick, one would think of a ReadWriteLock. However an Atomic might do too.

public class Holder {
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock r = lock.readLock();
    private final Lock w = lock.writeLock();

    private static Holder instance = new Holder();

    public static Data getData() {
        ...
    }

    public static Data setData() {
    }
}
Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
0

You can use 'synchronized' keyword (blocked)

class App {
    int var0 = 0;
    int var1 = 0;

    public synchronized void apply(IntUnaryOperator f0, IntUnaryOperator f1) {
        this.var0 = f0.applyAsInt(this.var0);
        this.var1 = f0.applyAsInt(this.var1);
    }

    public synchronized int[] get() {
        return new int[] {var0, var1};
    }
}

or 'Lock' class (blocked)

class App {
    Lock lock = new ReentrantLock();
    int var0 = 0;
    int var1 = 0;

    public void apply(IntUnaryOperator f0, IntUnaryOperator f1) {
        lock.lock();
        try {
            this.var0 = f0.applyAsInt(this.var0);
            this.var1 = f0.applyAsInt(this.var1);
        } finally {lock.unlock();}
    }

    public int[] get() {
        lock.lock();
        try {
            return new int[]{var0, var1};
        } finally {lock.unlock();}
    }
}

or use 'AtomicReference' to immutable data structure (non-blocked)

class App {
    AtomicReference<Data> data = new AtomicReference<>(new Data(0, 0));

    public void apply(IntUnaryOperator f0, IntUnaryOperator f1) {
        while (true) {
            Data oldData = data.get();
            Data newData = new Data(
                    f0.applyAsInt(oldData.var0),
                    f1.applyAsInt(oldData.var1)
            );
            if (data.compareAndSet(oldData, newData)) {
                break;
            }
        }
    }

    public int[] get() {
        Data current = data.get();
        return new int[]{current.var0, current.var1};
    }

    static class Data {
        final int var0;
        final int var1;

        public Data(int var0, int var1) {
            this.var0 = var0;
            this.var1 = var1;
        }
    }
}

or realize something like 'actor model' (non-blocking + with additional Thread) for writing and non-blocking atomic reading

class App {
    AtomicReference<Data> data = new AtomicReference<>(new Data(0, 0));
    BlockingQueue<IntUnaryOperator[]> mutateOperations
            = new LinkedBlockingQueue<>();
    Thread writer;
    {
        this.writer = new Thread(() -> {
            while (true) {
                try {
                    IntUnaryOperator[] mutateOp = mutateOperations.take();
                    Data oldData = data.get();
                    data.set(new Data(
                            mutateOp[0].applyAsInt(oldData.var0),
                            mutateOp[1].applyAsInt(oldData.var1)
                    ));
                } catch (InterruptedException e) {
                    break;
                }
            }
        });
        this.writer.start();
    }

    public void apply(IntUnaryOperator f0, IntUnaryOperator f1) {
        mutateOperations.add(new IntUnaryOperator[]{f0, f1});
    }

    public int[] get() {
        Data current = data.get();
        return new int[]{current.var0, current.var1};
    }

    static class Data {
        final int var0, var1;
        public Data(int var0, int var1) {
            this.var0 = var0;
            this.var1 = var1;
        }
    }
}
Ivan Golovach
  • 199
  • 2
  • 5
-3

There is AtomicInteger API in Java SE.

ilian
  • 60
  • 2
  • 3
  • 2
    How would using `AtomicInteger` solve the problem stated in the question? – Jesper Jul 13 '16 at 13:32
  • 1
    AtomicIntegers don't make a set of instructions atomic. – f1sh Jul 13 '16 at 13:35
  • Wrong answer I agree. But he could still use the java.util.concurrent.atomic and AtomicReference if the two var1 and var2 are combined in an object. – ilian Jul 13 '16 at 13:59
  • 1
    No, that would not work. An `AtomicReference` allows certain atomic operations to be performed _on the reference_, but it has no effect on how operations on the fields of the referenced object are performed. – Solomon Slow Jul 13 '16 at 14:05