I came across the following scenario when studying the book "Functional Programming in Scala" by Paul Chiusano and Runar Bjanarson (Ch. 7 - Purely functional parallelism).
package fpinscala.parallelism
import java.util.concurrent._
import language.implicitConversions
object Par {
type Par[A] = ExecutorService => Future[A]
def run[A](s: ExecutorService)(a: Par[A]): Future[A] = a(s)
def unit[A](a: A): Par[A] = (es: ExecutorService) => UnitFuture(a) // `unit` is represented as a function that returns a `UnitFuture`, which is a simple implementation of `Future` that just wraps a constant value. It doesn't use the `ExecutorService` at all. It's always done and can't be cancelled. Its `get` method simply returns the value that we gave it.
private case class UnitFuture[A](get: A) extends Future[A] {
def isDone = true
def get(timeout: Long, units: TimeUnit) = get
def isCancelled = false
def cancel(evenIfRunning: Boolean): Boolean = false
}
def map2[A,B,C](a: Par[A], b: Par[B])(f: (A,B) => C): Par[C] = // `map2` doesn't evaluate the call to `f` in a separate logical thread, in accord with our design choice of having `fork` be the sole function in the API for controlling parallelism. We can always do `fork(map2(a,b)(f))` if we want the evaluation of `f` to occur in a separate thread.
(es: ExecutorService) => {
val af = a(es)
val bf = b(es)
UnitFuture(f(af.get, bf.get)) // This implementation of `map2` does _not_ respect timeouts. It simply passes the `ExecutorService` on to both `Par` values, waits for the results of the Futures `af` and `bf`, applies `f` to them, and wraps them in a `UnitFuture`. In order to respect timeouts, we'd need a new `Future` implementation that records the amount of time spent evaluating `af`, then subtracts that time from the available time allocated for evaluating `bf`.
}
def fork[A](a: => Par[A]): Par[A] = // This is the simplest and most natural implementation of `fork`, but there are some problems with it--for one, the outer `Callable` will block waiting for the "inner" task to complete. Since this blocking occupies a thread in our thread pool, or whatever resource backs the `ExecutorService`, this implies that we're losing out on some potential parallelism. Essentially, we're using two threads when one should suffice. This is a symptom of a more serious problem with the implementation, and we will discuss this later in the chapter.
es => es.submit(new Callable[A] {
def call = a(es).get
})
def lazyUnit[A](a: => A): Par[A] = fork(unit(a))
def equal[A](e: ExecutorService)(p: Par[A], p2: Par[A]): Boolean =
p(e).get == p2(e).get
}
You can find the original code on Github here. See here for the java.util.concurrent documentation.
I am concerned with the implementation of fork. In particular, allegedly fork can lead to deadlocks when the ThreadPool is too small.
I consider the following example:
val a = Par.lazyUnit(42 + 1)
val es: ExecutorService = Executors.newFixedThreadPool(2)
println(Par.fork(a)(es).get)
I would not expect this example to end up in a deadlock as there are two threads. Yet, it does on my computer when I run it in the Scala REPL. Why is this so?
The output when initializing the ExecutorService is
es: java.util.concurrent.ExecutorService =
java.util.concurrent.ThreadPoolE
xecutor#73a86d72[Running, pool size = 0, active threads = 0, queued tasks =
0, completed tasks = 0]
Is pool size = 0 correct here? In other word, is this a problem of not understanding java.util.concurrent._ or is the issue with not understanding the Scala part?
OK, after a long investigation I believe I have an answer. The full story is long but I'll try to shorten it by simplifying and avoiding many details.
Note: Potentially Scala can be compiled to various different target platforms but this particular issue happened on the Java/JVM as the target so this is what this answer is about.
The deadlock you see has nothing to do with the size of the thread pool. Actually it is the outer fork call that hangs. It is related to a combination of REPL implementation details and multi-threading but it takes learning a few pieces to understand how it happens:
how Scala REPL works
how Scala compiles objects to Java/JVM
how Scala emulates the by-name parameters on Java/JVM
how Java/JVM runs the static initializers of classes
A short(er) version (see also Summary at the end) is that this code hangs under a REPL because when it is being executed by the REPL, it is logically similar to the following code:
object DeadLock {
import scala.concurrent._
import scala.concurrent.duration.Duration
import scala.concurrent.ExecutionContext.Implicits.global
val foo: Int = Await.result(Future(calc()), Duration.Inf)
def printFoo(): Unit = {
println(s"Foo = $foo")
}
private def calc(): Int = {
println("Before calc")
42
}
}
def test(): Unit = {
println("Before printFoo")
DeadLock.printFoo()
println("After printFoo")
}
or very similar in the Java world:
class Deadlock {
static CompletableFuture<Integer> cf;
static int foo;
public static void printFoo() {
System.out.println("Print foo " + foo);
}
static {
cf = new CompletableFuture<Integer>();
new Thread(new Runnable() {
#Override
public void run() {
calcF();
}
}).start();
try {
foo = cf.get();
System.out.println("Future result = " + cf.get());
} catch (InterruptedException e) {
e.printStackTrace();f
} catch (ExecutionException e) {
e.printStackTrace();
}
}
private static void calcF() {
cf.complete(42);
}
}
public static void main(String[] args) {
System.out.println("Before foo");
Deadlock.printFoo();
System.out.println("After foo");
}
If it is clear to you why this code deadlocks, you already know most of the story and probably can deduce the rest yourself. You might just glance over the Summary section at the end.
How Java static initializer can deadlock?
Let's start from the end of this story: why the Java code hangs? It happens because of the two guarantees of the Java/JVM for the static initializer (for more details see the section 12.4.2. Detailed Initialization Procedure of the JLS):
the static initializer will be run before any other "external" use of the class
the static initializer will be run exactly once and it is done via global locking
The lock used for the static initializer is implicit and managed by the JVM but it is there. It means that the code logically is similar to something like this:
class Deadlock {
static boolean staticInitFinished = false;
// unique value for each thread!
static ThreadLocal<Boolean> currentThreadRunsStaticInit = ThreadLocal.withInitial(() -> Boolean.FALSE);
static CompletableFuture<Integer> cf;
static int foo;
static void enforceStaticInit() {
synchronized (Deadlock.class) {
// is init finished?
if (staticInitFinished)
return;
// are we the thread already running the init?
if(currentThreadRunsStaticInit.get())
return;
currentThreadRunsStaticInit.set(true);
cf = new CompletableFuture<Integer>();
new Thread(new Runnable() {
#Override
public void run() {
calcF();
}
}).start();
try {
foo = cf.get();
System.out.println("Future result = " + cf.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
currentThreadRunsStaticInit.set(false);
staticInitFinished = true;
}
}
private static void calcF() {
enforceStaticInit();
cf.complete(42);
}
public static void printFoo() {
enforceStaticInit();
System.out.println("Print foo " + foo);
}
}
Now it is pretty clear why this code deadlocks: Our static initializer starts a new thread and blocks waiting for result from it. But that new thread tries to access the same class (the calcF method) and being another thread it has to wait for the already running static initializer to finish. Note that if the calcF method was in another class, everything would work just fine.
How Scala REPL works
Now let's get back to the start of the story on how Scala REPL works. This answer is a great simplification of the real deal but it captures the important for this situation details. Luckily for the REPL implementors the Scala compiler is written in Scala. It means the REPL doesn't have to somehow interpret the code, it can run it through the standard compiler and then run the compiled code via Java Reflection API. This still takes some decoration of the code to make the compiler happy and to get the results back.
Simplifying it a bit (or well, a lot), when you type something like
val a = Par.lazyUnit(42 + 1)
into the REPL the code is analyzed and transformed into something like this:
package line3
object read {
val a = Par.lazyUnit(42 + 1)
val res3 = a
}
object eval {
def print() = {
println("a: Par.Par[Int] = " + read.res3)
}
}
and then line3.eval.print() is called via reflection.
A similar story happens for:
val es: ExecutorService = Executors.newFixedThreadPool(2)
and finally when you do
Par.fork(a)(es).get
the things get a bit more interesting because you have a dependency on the previous lines that is cleverly implemented using imports:
package line5
object read {
import line2.read.Par
import line3.read.a
import line4.read.es
val res5 = Par.fork(a)(es).get
}
object eval {
def print() = {
println("res5: Int = " + read.res5)
}
}
The important thing here is that everything you write into REPL is wrapped into a brand new object and then compiled and run as a usual code.
How Scala emulates by-name parameters on Java/JVM
The definition of the fork method uses a by-name parameter:
def fork[A](a: => Par[A]): Par[A] =
Here it is used to evaluate a lazily that is crucial to the whole logic of the fork. The Java/JVM has not standard support for lazy evaluation but it can be emulated and this is what the Scala compiler does. Internally the signature is changed to use a Function0:
def fork[A](aWrapper: () => Par[A]): Par[A] =
and every access to a is substituted with a call to aWrapper.apply(). Another part of the magic happens on the caller-side of a method with a by-name parameter: there the parameter should also be wrapped into a Function0 so code becomes something like
object read {
import line2.read.Par
import line3.read.a
import line4.read.es
val res5 = Par.fork(() => a)(es).get
}
But actually it is a bit different. Naively it would take another class just for this small function and this feels wasteful for such a simple logic. In practice in the Scala 2.12 the magic of the Java 8 LambdaMetafactory is used so the code really becomes something like
object read {
import line2.read.Par
import line3.read.a
import line4.read.es
def aWrapper():Int = a
val res5 = Par.fork(aWrapper _)(es).get
}
where aWrapper _ signifies converting a method to a Funciton0 that is done with the LambdaMetafactory. As you might suspect from the chapter on the Java static initializer deadlock, introduction of the def aWrapper is a crucial difference. You already can see that this code is very similar to the first Scala snippet in the answer that hangs.
How Scala compiles the object on Java/JVM
The final piece of the puzzle is how Scala object is compiled in Java/JVM. Well it is actually compiled to something similar to a "static class" but since you can use object as an object parameter, it has to be a bit more complicated. In reality all the initialization logic is moved to the constructor of the object class and there is simple static initializer that calls it. So our last read object in Java would (ignoring the imports) look like this:
class read$ {
static read$ MODULE$
static {
new read$()
}
private Par[Int] res5;
private read$() {
MODULE$ = this;
res5 = Par.fork(read$::aWrapper)(es).get
}
private static int aWrapper(){
return line3.read$.MODULE$.a;
}
}
here again read$::aWrapper signifies building a Function0 form the aWrapper method using the LambdaMetafactory. In other words the initialization of the Scala object is translated into a code that is run as a part of the Java static initializer.
Summary
To sum up how things get screwed:
REPL turns your code into a new object for each line and compiles it
object initialization logic is translated into a Java static initialization logic
call of a method with a by-name parameter in simple cases is translated into a method that wraps the "return the value" logic and that method is added to the same class or object
Par.fork being executed as a part of the object initialization (i.e. a part of the Java static initializer) tries to evaluate the by-name parameter (i.e. calls the method on the same class) on a different thread and blocks waiting for the result of that thread
Java static initializer is logically executed under a global lock so it blocks that different thread calling the method. But it is itself blocked waiting for that method call to finish.
Related
Problem
In Java Official Doc, it says
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs)
Returns a new CompletableFuture that is completed when all of the given CompletableFutures complete. If any of the given CompletableFutures complete exceptionally, then the returned CompletableFuture also does so, with a CompletionException holding this exception as its cause.
The doc doesn't specify the case when multiple given CompletableFutures complete exceptionally. For example, in the following code snippet, what will the exception and its cause be if c1, c2, c3 all complete exceptionally?
CompletableFuture.allOf(c1, c2, c3)
.whenComplete((result, exception) -> {
if (exception != null) {
System.out.println("exception occurs");
System.err.println(exception);
} else {
System.out.println("no exception, got result: " + result);
}
})
My experiment 1
Create a completableFuture signal_1 and signal_2 that both completes exceptionally fast. The output shows signal_1 gets passed to .whenComplete() as the cause of exception.
package com.company;
import java.util.concurrent.*;
public class Main {
private static void runTasks(int i) {
System.out.printf("-- input: %s --%n", i);
CompletableFuture<Void> signal_1 = new CompletableFuture<>();
signal_1.completeExceptionally(new RuntimeException("Oh noes!"));
CompletableFuture<Integer> signal_2 = CompletableFuture.supplyAsync(() -> 16 / i);
CompletableFuture.allOf(signal_1, signal_2)
.thenApplyAsync(justVoid -> {
final int num = signal_2.join();
System.out.println(num);
return num;
})
.whenComplete((result, exception) -> {
if (exception != null) {
System.out.println("exception occurs");
System.err.println(exception);
} else {
System.out.println("no exception, got result: " + result);
}
})
.thenApplyAsync(input -> input * 3)
.thenAccept(System.out::println);
}
public static void main(String[] args) {
runTasks(0);
}
}
Output
-- input: 0 --
exception occurs
java.util.concurrent.CompletionException: java.lang.RuntimeException: Oh noes!
Process finished with exit code 0
My experiment 2
Added a 3 second sleep before signal_1 completes exceptionally, so signal_1 should completes after signal_2. However, the output still shows signal_1 gets passed to .whenComplete() as the cause of exception.
package com.company;
import java.util.concurrent.*;
public class Main {
static ExecutorService customExecutorService = Executors.newSingleThreadExecutor();
private static void runTasks(int i) {
System.out.printf("-- input: %s --%n", i);
CompletableFuture<Void> signal_1 = CompletableFuture.supplyAsync(() -> {
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
throw new RuntimeException("Oh noes!");
}, customExecutorService);
CompletableFuture<Integer> signal_2 = CompletableFuture.supplyAsync(() -> 16 / i);
CompletableFuture.allOf(signal_1, signal_2)
.thenApplyAsync(justVoid -> {
final int num = signal_2.join();
System.out.println(num);
return num;
})
.whenComplete((result, exception) -> {
if (exception != null) {
System.out.println("exception occurs");
System.err.println(exception);
} else {
System.out.println("no exception, got result: " + result);
}
})
.thenApplyAsync(input -> input * 3)
.thenAccept(System.out::println);
}
public static void main(String[] args) {
runTasks(0);
customExecutorService.shutdown();
}
}
Output
-- input: 0 --
exception occurs
java.util.concurrent.CompletionException: java.lang.RuntimeException: Oh noes!
Process finished with exit code 0
This is largely a repeat of what VGR said in the comments, but it is an important rule of thumb that deserves a full write-up.
In Java, there is an important concept called Unspecified Behaviour. In short, if the docs do not explicitly define what happens in a specific scenario, then the implementation is free to do literally whatever it chooses to, within reason and within the bounds of the other rules that are explicitly defined. This is important because there are several different manifestations of that.
For starters, the result could be platform specific. For some machines, leaving the behaviour undefined allows for some special optimizations that still return a "correct" result. And since Java prioritizes both similar/same behaviour on all platforms as well as performance, choosing not to specify certain aspects of execution allows them to stay true to that promise while still getting the optimization benefits that come with specific platforms.
Another example is when the act of unifying behaviour into a specific action is not currently feasible. If I had to guess, this is most likely what Java is actually doing. In certain instances, Java will design a component with the potential for certain functionality, but will stop short of actually defining and implementing it. This is usually done in instances where building out a full blown solution would be more effort than it is worth, amongst other reasons. Ironically enough, CompletableFuture itself is a good example of this. Java 5 introduced Future's, but only as an interface with generic functionality. CompletableFuture, the implmementation which came in Java 8, later fleshed out and defined all the unspecified behaviour left over from the Java 5 interface.
And lastly, they may avoid defining specified behaviour if choosing specified behaviour would stifle the flexibility of possible implementations. Currently, the method you showed does not have any specified behaviour about which exception will be thrown when the futures fail. This allows any class that later extends CompletableFuture to be able to specify that behaviour for themselves while still maintaining Liskov's Substitution Principle. If you don't already know, LSP says that if a Child class extends a Parent class, then that Child class must follow all the rules of the Parent class. As a result, if the rules (specified behaviour) of the class are too restrictive, then you prevent future implementations/extensions of this class from being able to function without breaking LSP. There are likely some extensions for CompletableFuture that allow you to define exactly what type of Exception is thrown when calling the method. But that's the point - they are extensions that you can choose to opt-in to. If they define it for you, then you are stuck with it unless you implement it yourself, or you go outside the languages standard library.
When I was writing node.js app, I happened to see the code like below:
var Mutex = require('async-mutex').Mutex;
const mutex = new Mutex();
const release = await mutex.acquire();
try {
// ...
} finally {
release();
}
In Java world, this kind of mutex implementation would be something like this:
private Semaphore mutex = new Semaphore(1);
try {
mutex.acquire();
} catch (InterruptedException e) {
// exception handling code
} finally {
mutex.release();
}
From these examples, can I assume that new Mutex() and new Semaphore(1) are both instantiating new instances on memory?
I want to know the exact lifecycle of an object that is created by this new keyword in Node.js world.
UPDATE:
Thank you for all the useful comments. I have one more question. In express app, I'm writing a code something like this:
'use strict'
const express = require('express');
const router = express.Router();
const co = require('co');
const models = require('models');
const Mutex = require('async-mutex').Mutex;
const CharacterCounter = models.CharacterCounter;
const mutex = new Mutex();
router.get('/message', function (req, res, next) {
co(function* () {
const originalText = req.body.message;
const release = yield mutex.acquire();
let addedCount = 0;
try {
const { currentCount } = yield CharacterCounter.findOne(); // get total character count so far
addedCount = currentCount + originalText.length;
yield CharacterCounter.updateTotalCharacterCount(addedCount); // save on DB
} catch (e) {
console.log(e);
} finally {
release();
}
// ... my code is keep going
This is a text counter API, we send requests with messages in body to this API and count up the number of characters of each message. Then finally save it on DB. So I have to use mutex library to handle concurrent requests. If we make 100 requests at the same time and each of them has a message with a length of 10, total character count on DB should be 1000(100reqs * 10characters).
I tried this but got the wrong result. Instead of the expected result of 1000, it's been always around 450.
When looking console, it seems that the mutex object is instantiated several times. I thought this mutex object must be a singleton, initialised only once when a server is started and used for the rest of the process. I now know this new keyword creates an object on memory like Java, so this behaviour sounds strange.
Why Node.js does not use an already created mutex object and create another one? Is there some logic in Node.js creating new instance automatically when massive concurrent requests come?
The new keyword in Javascript (Node.js is runtime environment for Javascript, think Node.js is for Javascript as JRE is for Java) is paired with the class keyword.
These were introduced to add some familiarity for developers coming from other languages. The class syntax was introduced in ES6 and is just another way to create an object, most things in Javascript are objects.
Inside a class definition, a developer will write something like this:
class MyClass {
constructor(opts) {
doSomethingWithOpts(opts);
this.opts = opts;
}
myMethod() {
// do something here
}
}
When you call const myClass = new MyClass(opts); it is calling the constructor function inside the class and returning a new copy of the class (which is an object). Prior to the ES6 class keyword, it was common to create new objects (which can be thought of in a similar fashion to a class) using something like this (called a factory):
const newObject = (opts) => {
doSomethingWithOpts(opts);
return {
opts,
myMethod() {
// do something here
},
};
};
This factory would be called in your code like so: const myObject = newObject(opts); - notice the distinct lack of the new keyword in this version
Both variations of creating an object will add the object to memory stack, but once you are finished with it, the garbage collector will clean it up (provided the conditions for garbage collection are met).
Hopefully that helps clarify a little!
... vs using a callback?
First example, with callback
public class NewClass {
public static final ScheduledExecutorService SCHEDULED_EXECUTOR = Executors.newScheduledThreadPool(1);
#FunctionalInterface
public interface F_CallbackDef {
void callback(String s);
}
public void a() {
b((String s) -> {
System.out.println(s);
});
}
public void b(F_CallbackDef callback) {
SCHEDULED_EXECUTOR.schedule(() -> {
String s = "string";
Random random = new Random();
boolean cond = random.nextBoolean();
if (cond) {
boolean cond2 = random.nextBoolean();
if (cond2) {
// assume possible uncatched exeption
}
callback.callback(s);
// in every case:
// callback just moves out of scope - no problem
}
}, 1, TimeUnit.HOURS);
}
}
Second example, with CompletableFuture
public class NewClass1 {
public static final ScheduledExecutorService SCHEDULED_EXECUTOR = Executors.newScheduledThreadPool(1);
public void a() {
CompletableFuture<String> cf = b();
cf.thenAcceptAsync((String s) -> {
System.out.println(s);
});
// cf moves out of scope immediatly
// but also it gets never completed, nor cancelled
}
CompletableFuture<String> b() {
CompletableFuture<String> result = new CompletableFuture<>();
SCHEDULED_EXECUTOR.schedule(() -> {
String s = "string";
Random random = new Random();
boolean cond = random.nextBoolean();
if (cond) {
boolean cond2 = random.nextBoolean();
if (cond2) {
// assume possible uncatched exception
}
result.complete(s);
}
// assume cond = false or cond2 = false
// this is not about that result.cancel() SHOULD be called, it is a bug if you will
}, 1, TimeUnit.HOURS);
return result;
}
}
Question are:
What happens to cf in example 2 / (When) Will it be removed by garbage collection?
Is there any better way to "translate" example 1 into example 2?
In your answer, please kindly stay sharp to technical arguments and not to preferred/improvable code (because that is not what this question is about), nor is it about that cf.cancel does not get called.
What happens to the CompletableFuture? When will it be removed by garbage collection?
In your code, you have 3 references to the CompletableFuture:
the local variable cf in a()
the local variable result in b()
the captured reference in the lambda passed to SCHEDULED_EXECUTOR.schedule()
Also note that the lambda that is passed to thenAcceptAsync() needs to be kept at least as long as the completable future is uncompleted, except if the completable future itself is gc'd. So even if you don't keep a reference to it, this lambda might live longer than the execution of the method.
Nothing special applies concerning CompletableFutures, standard GC rules apply: it will become eligible for garbage collection when nothing references it anymore – or more precisely when there exists no path to the GC roots anymore.
For local variables it's quite easy to see when they stop referencing it. For the captured reference, unfortunately it will remain as long as the lambda lives (even if it does not use it anymore). Assuming optimization, the lambda will be eligible for GC when no more executions are planned – so here it would be when you shutdown your executor.
Once those references are gone, it will thus be eligible for GC.
Is there any better way to "translate" example 1 into example 2?
The translation is not actually correct because a CompletableFuture can be completed only once. In example 1, your callback will be called for each successful execution, but in example 2, only the first successful one will trigger it. The next calls to result.complete(s) will actually be ignored.
CompletableFuture is not designed to handle a flow of result values. For that you should go towards reactive programming with observables and subscribers.
If this was for a single execution, the conversion would be correct, however it would be better to just rely on CompletableFuture.supplyAsync() than handling the future yourself. This would also automatically provide exception handling features.
In example 1, when you call a it calls b that schedules a task, and a returns. At some point the the callback will be executed by the thread that is running a task created inside b.
In example 2, when you call a it calls b that schedules a task and a returns. Code inside thenAcceptAsync is never executed, because you've never called that Future's get. cf is a local variable, so the object it points to becomes available for GC after a terminates. At some point GC may remove it.
If your were to call get somewhere in your code, it would block until the task would have completed, and then execute the clause in thenAcceptAsync in the context of the thread that is calling get.
There is no way to translate example 1 to example 2, because they are doing 2 quite different things, each of which may be correct depending on your requirements.
One other thing that you seem to be confused about: futures are neither completed nor cancelled, the tasks that they are pointing to ARE. They only provide the ability to get to the results of the task they are pointing to in the future.
The subject
I have some code that is decidedly not thread safe:
public class ExampleLoader
{
private List<String> strings;
protected List<String> loadStrings()
{
return Arrays.asList("Hello", "World", "Sup");
}
public List<String> getStrings()
{
if (strings == null)
{
strings = loadStrings();
}
return strings;
}
}
Multiple threads accessing getStrings() simultaneously are expected to see strings as null, and thus loadStrings() (which is an expensive operation) is triggered multiple times.
The problem
I wanted to make the code thread safe, and as a good citizen of the world I wrote a failing Spock spec first:
def "getStrings is thread safe"() {
given:
def loader = Spy(ExampleLoader)
def threads = (0..<10).collect { new Thread({ loader.getStrings() })}
when:
threads.each { it.start() }
threads.each { it.join() }
then:
1 * loader.loadStrings()
}
The above code creates and starts 10 threads that each calls getStrings(). It then asserts that loadStrings() was called only once when all threads are done.
I expected this to fail. However, it consistently passes. What?
After a debugging session involving System.out.println and other boring things, I found that the threads are indeed asynchronous: their run() methods printed in a seemingly random order. However, the first thread to access getStrings() would always be the only thread to call loadStrings().
The weird part
Frustrated after quite some time spent debugging, I wrote the same test again with JUnit 4 and Mockito:
#Test
public void getStringsIsThreadSafe() throws Exception
{
// given
ExampleLoader loader = Mockito.spy(ExampleLoader.class);
List<Thread> threads = IntStream.range(0, 10)
.mapToObj(index -> new Thread(loader::getStrings))
.collect(Collectors.toList());
// when
threads.forEach(Thread::start);
threads.forEach(thread -> {
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
// then
Mockito.verify(loader, Mockito.times(1))
.loadStrings();
}
This test consistently fails due to multiple calls to loadStrings(), as was expected.
The question
Why does the Spock test consistently pass, and how would I go about testing this with Spock?
The cause of your problem is that Spock makes methods it spies on synchronized. Particularly, the method MockController.handle(), through which all such calls go, is synchronized. You'll easily notice it if you add a pause and some output to your getStrings() method.
public List<String> getStrings() throws InterruptedException {
System.out.println(Thread.currentThread().getId() + " goes to sleep");
Thread.sleep(1000);
System.out.println(Thread.currentThread().getId() + " awoke");
if (strings == null) {
strings = loadStrings();
}
return strings;
}
This way Spock inadvertently fixes your concurrency problem. Mockito obviously uses another approach.
A couple of other thoughts on your tests:
First, you don't do much to ensure that all your threads have come to the getStrings() call at the same moment, thus decreasing the probability of collisions. Long time may pass between threads start (long enough for the first one to complete the call before others start it). A better approach would
be to use some synchronization primitive to remove the influence of threads startup time. For instance, a CountDownLatch may be of use here:
given:
def final CountDownLatch latch = new CountDownLatch(10)
def loader = Spy(ExampleLoader)
def threads = (0..<10).collect { new Thread({
latch.countDown()
latch.await()
loader.getStrings()
})}
Of course, within Spock it will make no difference, it's just an example on how to do it in general.
Second, the problem with concurrency tests is that they never guarantee that your program is thread safe. The best you can hope for is that such test will show you that your program is broken. But even if the test passes, it doesn't prove thread safety. To increase chances of finding concurrency bugs you may want to run the test many times and gather statistics. Sometimes, such tests only fail once in several thousands or even once in several hundreds thousands of runs. Your class is simple enough to make guesses about its thread safety, but such approach will not work with more complicated cases.
for learning purpose i have tried to implements a queue data-structure + Consumer/producer chain that is thread-safe, for learning purpose too i have not used notify/wait mechanism :
SyncQueue :
package syncpc;
/**
* Created by Administrator on 01/07/2009.
*/
public class SyncQueue {
private int val = 0;
private boolean set = false;
boolean isSet() {
return set;
}
synchronized public void enqueue(int val) {
this.val = val;
set = true;
}
synchronized public int dequeue() {
set = false;
return val;
}
}
Consumer :
package syncpc;
/**
* Created by Administrator on 01/07/2009.
*/
public class Consumer implements Runnable {
SyncQueue queue;
public Consumer(SyncQueue queue, String name) {
this.queue = queue;
new Thread(this, name).start();
}
public void run() {
while(true) {
if(queue.isSet()) {
System.out.println(queue.dequeue());
}
}
}
}
Producer :
package syncpc;
import java.util.Random;
/**
* Created by Administrator on 01/07/2009.
*/
public class Producer implements Runnable {
SyncQueue queue;
public Producer(SyncQueue queue, String name) {
this.queue = queue;
new Thread(this, name).start();
}
public void run() {
Random r = new Random();
while(true) {
if(!queue.isSet()) {
queue.enqueue(r.nextInt() % 100);
}
}
}
}
Main :
import syncpcwn.*;
/**
* Created by Administrator on 27/07/2015.
*/
public class Program {
public static void main(String[] args) {
SyncQueue queue = new SyncQueue();
new Producer(queue, "PROCUDER");
new Consumer(queue, "CONSUMER");
}
}
The problem here, is that if isSet method is not synchronized , i got an ouput like that :
97,
55
and the program just continue running without outputting any value. while if isSet method is synchronized the program work correctly.
i don't understand why, there is no deadlock, isSet method just query the set instance variable without setting it, so there is no race condition.
set needs to be volatile:
private boolean volatile set = false;
This ensures that all readers see the updated value when a write completes. Otherwise they will end up seeing the cached value. This is discussed in more detail in this article on concurrency, and also provides examples of different patterns that use volatile.
Now the reason that your code works with synchronized is probably best explained with an example. synchronized methods can be written as follows (i.e., they are equivalent to the following representation):
public class SyncQueue {
private int val = 0;
private boolean set = false;
boolean isSet() {
synchronized(this) {
return set;
}
}
public void enqueue(int val) {
synchronized(this) {
this.val = val;
set = true;
}
}
public int dequeue() {
synchronized(this) {
set = false;
return val;
}
}
}
Here, the instance is itself used as a lock. This means that only thread can hold that lock. What this means is that any thread will always get the updated value because only one thread could be writing the value, and a thread that wants to read set won't be able to execute isSet until the other thread releases the lock on this, at which point the value of set will have been updated.
If you want to understand concurrency in Java properly you should really read Java: Concurrency In Practice (I think there's a free PDF floating around somewhere as well). I'm still going through this book because there are still many things that I do not understand or am wrong about.
As matt forsythe commented, you will run into issues when you have multiple consumers. This is because they could both check isSet() and find that there is a value to dequeue, which means that they will both attempt to dequeue that same value. It comes down to the fact that what you really want is for the "check and dequeue if set" operation to be effectively atomic, but it is not so the way you have coded it. This is because the same thread that initially called isSet may not necessarily be the same thread that then calls dequeue. So the operation as a whole is not atomic which means that you would have to synchronize the entire operation.
The problem you have is visibility (or rather, the lack of it).
Without any instructions to the contrary, the JVM will assume that the value assigned to a variable in one thread need not be visible to the other threads. It may be made visible sometimes later (when it's convenient to do so), or maybe not ever. The rules governing what should be made visible and when are defined by the Java Memory Model and they're summed up here. (They may be a bit dry and scary at first, but it's absolutely crucial to understand them.)
So even though the producer sets set to true, the consumer will continue to see it as false. How can you publish a new value?
Mark the field as volatile. This works well for primitive values like boolean, with references you have to be a bit more careful.
synchronized provides not just mutual exclusion but also guarantees that any values set in it will be visible to anyone entering a synchronized block that uses the same object. (This is why everything works if you declare the isSet() method synchronized.)
Using a thread-safe library class, like the Atomic* classes of java.util.concurrent
In your case volatile is probably the best solution because you're only updating a boolean, so atomicity of the update is guaranteed by default.
As #matt forsythe pointed out, there is also a TOCTTOU issue with your code too because your threads can be interrupted by another between isSet() and enqueue()/dequeue().
I assume that when we get stuck in threading issue, the first step was to make sure that both the threads are running well. ( i know they will as there are no locks to create deadlock)
For that you could have added a printf statement in enqueue function as well. That would make sure that enqueue and dequeue threads are running well.
Then second step should have been that "set" is the shared resource, so is the value toggling well enough so that code can run in desired fashion.
I think if you could reason and put the logging well enough, you can realize the issues in problem.