3

I cannot decrement a common Long-value within a stream (outer declared value used within streams must be final), so I must use AtomicLong within java streams:

var users = Stream<User> getUsers();
var dec = new AtomicLong(10);
long dec = 10;

// users is a 
users.forEach(u->{

// does not work within streams, so I have to use AtomicLong
dec--;

// this works
dec.decrementAndGet();


// which one should I use, if I only want to get the raw value?
long actualValue = dec.getPlain();
long actualValue = dec.get(); // the same as dec.getOpaque();

});

I cannot see any differences between dec.getPlain() and dec.get(). I dont understand the

with memory semantics of reading

described in the API. Where lies the differences in those methods?

Which should I use if I only have one thread which reads the actualValue.

nimo23
  • 5,170
  • 10
  • 46
  • 75
  • It's not really related to streams, but it's an interesting point. `getPlain` was introduced in Java9, and is related to how variables are shared among threads. I haven't had the occasion yet to dig into what this does exactly. – njzk2 Sep 12 '19 at 16:17
  • 6
    You should not use side effects when you try to program in a functional way. – Donat Sep 12 '19 at 16:17
  • It seems like this would be easily solved by using a `for` loop instead of a Stream. – VGR Sep 12 '19 at 16:18
  • 4
    You shouldn’t use any of them. Abusing an `AtomicLong` for stateful operation inside a Stream operation is an anti pattern. Your confusion about the memory modes is a good indicator for the fact that you are using a tool for something it wasn’t meant for. – Holger Sep 12 '19 at 16:18
  • @Donat I need a decrement value, any suggestions how to do that without "side effects"? – nimo23 Sep 12 '19 at 16:19
  • @Holger so you say, I should use common `for`-loop instead of streams, if I need a decrement value? – nimo23 Sep 12 '19 at 16:20
  • 6
    Use an ordinary `for` loop. Or just `long dec = 10 - users.size();` The latter demonstrates how the “need to decrement a value” is an [xy problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem/66378#66378). You never need to decrement a value. Some subsequent code might need the resulting value. Decrementing a counter for each element is just one possible approach to get there. Not necessarily the best. Another is the `count()` method on streams. – Holger Sep 12 '19 at 16:20
  • Related (not a dupe): [Variable used in lambda expression should be final or effectively final](https://stackoverflow.com/questions/34865383/variable-used-in-lambda-expression-should-be-final-or-effectively-final) – Ole V.V. Sep 12 '19 at 16:24
  • The `users` is actually a stream, so I have to collect it to a list to do a ordinary for-loop - a overhead. Actually, I can avoid that overhead by using AtomLong. I dont know why this is a AntiPattern.. – nimo23 Sep 12 '19 at 16:26
  • @Holger `long dec = 10 - users.size();` is not an option because I need the actual decremented value within each iteration. – nimo23 Sep 12 '19 at 16:31
  • It boils down to the difference between reading a volatile variable (`get()`) and reading a non-volatile variable (`getPlain()`). Some synchronisation actions are required when you do volatile read/write. The Java model memory ([JSL 17.4.](https://docs.oracle.com/javase/specs/jls/se12/html/jls-17.html#jls-17.4)) explains it in detail. – Andrew Tobilko Sep 12 '19 at 16:33
  • @AndrewTobilko so for my case, `getPlain()` is sufficient..however, now I am in doubt to use atomicLong within streams. – nimo23 Sep 12 '19 at 16:36
  • 4
    `for(Iterator it = users.iterator(); it.hasNext(); ) { User u = i.next(); /* rest of the loop body */ }` works without collecting the stream into a `List`. Perhaps there are alternatives to the loop. But finding them requires knowledge about the actual purpose of the operation. – Holger Sep 12 '19 at 16:41
  • @Holger good idea. The iterator avoids the overhead of collecting the stream to a list while I can also avoid the use of AtomicLong. I would accept this as an answer. I dont find other alternatives. The actual purpose of the operation is simple: I only need a typical decrement value for a method called within each iteration. However, iterator is a good idea. Thanks! – nimo23 Sep 12 '19 at 16:50
  • 1
    @GhostCat `users` is a stream of users (`Stream users;`). – nimo23 Sep 12 '19 at 19:56

1 Answers1

2

The difference is the method for fetching the result from memory. I. e. if it is read as if declared as volatile or as a normal variable. volatile is for variables which are being accessed concurrently by more than one thread. When normal varaibles are being changed by one thread, the other threads may never see the change because each thread may hold the variable in its own cache. volatile variables must always be read and written from common memory (not cache). This makes access slower but reliable when used concurrently.

When you do not use the value concurrently, the method getPlain() is sufficient and it should be faster. But using AtomicLong for side effects with a stream is kind of a misuse because when programming in a functional way, you should not depend on side effects.

Donat
  • 4,157
  • 3
  • 11
  • 26