I have some class which is not thread safe:
class ThreadUnsafeClass {
long i;
long incrementAndGet() { return ++i; }
}
(I've used a long
as the field here, but we should think of its field as being some thread-unsafe type).
I now have a class which looks like this
class Foo {
final ThreadUnsafeClass c;
Foo(ThreadUnsafeClass c) {
this.c = c;
}
}
That is, the thread unsafe class is a final field of it. Now I'm going to do this:
public class JavaMM {
public static void main(String[] args) {
final ForkJoinTask<ThreadUnsafeClass> work = ForkJoinTask.adapt(() -> {
ThreadUnsafeClass t = new ThreadUnsafeClass();
t.incrementAndGet();
return new FC(t);
});
assert (work.fork().join().c.i == 1);
}
}
That is, from thread T
(main), I invoke some work on T'
(the fork-join-pool) which creates and mutates an instance of my unsafe class and then returns the result wrapped in a Foo
. Please note that all mutation of my thread unsafe class happens on a single thread, T'
.
Question 1: Am I guaranteed that the end-state of the instance of the thread-unsafe-class is safely ported across the T' ~> T
thread boundary at the join
?
Question 2: What if I had done this using parallel streams? For example:
Map<Long, Foo> results =
Stream
.of(new ThreadUnsafeClass())
.parallel()
.map(tuc -> {
tuc.incrementAndGet();
return new Foo(tuc);
})
.collect(
Collectors.toConcurrentMap(
foo -> foo.c.i,
Function.identity();
)
);
assert(results.get(1) != null)
final
should make not difference. Have you looked at the Atomic options injava.util.concurrent.atomic
? – Binocularslong
introduces additional problem complexity since datatypeslong
anddouble
are the only types that are not guaranteed to be written/read atomically. This means that in theory, one thread could read the first half of thelong
, another thread could update thelong
and then the first thread could read the second (updated) half of thelong
. In the given scenario, this has no effect. But having multipleThreads
executingincrementAndGet()
could lead to very messy situations. – ToadyThreadUnsafeClass
is created and mutated only from within a single threadT'
. After mutation has finished it is passed across a thread boundary. The question is: are all mutations inT'
subsequently visible toT
? – AppoloniaincrementAndGet()
and the access toi
and you do not accessi
beforehand, thus caching should not be an issue. – Toady