32. RACE CONDITION
▸ definition: shared resources may get used “at the same
time” by different threads, resulting in a invalid state.
▸ motivation: any need of concurrent or parallel processing.
▸ how to avoid: usage of some mechanism to ensure
resources are used by only one thread at a time or even
share nothing.
35. thread 1 thread 2
VAR=0
VAR++ VAR++
VAR=1
Clearly not the
expected result. There
are code in production
working with those
errors for years without
people realising it.
VAR WAS NOT
SYNCHRONISED PROPERLY
36. AVOIDING OR FIXING THIS RACE CONDITION
▸ let the database deal with it (just kidding, but sadly it seems to
be the standard way of doing it).
▸ correct synchronisation by using locks.
▸ usage of concurrent classes, such as AtomicLong.
▸ one counter per thread (summing them still requires
synchronisation).
▸ share nothing.
▸ any other suggestion?
40. thread 1 thread 2
VAR=0LOCK
VAR++
VAR++
VAR=2
The result was as
expected, but there
was a penalty in the
time it took to perform
both operations. In
order to minimise it
avoid sharing in the
first place.
VAR WAS
PROPERLY SYNCHRONISED
47. LESSONS
▸ Synchronise properly. High level APIs are easier not to
mess with. java.util.concurrent excels at that.
▸ The optimal number of threads is usually twice the number
of cores: Runtime.getRuntime().availableProcessors() * 2;
▸ Measure and stress. It is not easy to see synchronisation
issues, since the behaviour varies depending on machine,
operation system, etc. They usually don’t show while
debugging.
49. DEAD LOCKS
▸ what it is: threads holding and waiting each other locks.
▸ motivation: global lock leads to global contention and
slow code. Use of more than one fine grained lock at the
same time in more than one thread in a unpredictable way
is the real problem.
▸ how to avoid: ensure same locking order or review
synchronisation strategy (functional approach, atomic
classes, high level APIs, concurrent collections, share
nothing, etc).
50. thread 1 thread 2
AB
Two threads have
access to resources
protected by two
distinct locks: A and B.
Green means available,
yellow means waiting
and red means locked.
Two scenarios are
going to be presented:
Threads acquiring the
locks in the same order,
and in different order.
59. thread 1 thread 2
AB
Then lock A is released.
No synchronisation
problems has
happened and no
locked resources where
harmed in this
execution. Some
contention has
happened, but they
where temporary.
EVERYTHING WAS FINE.
62. thread 1 thread 2
A B
And the second thread
acquires lock B.
63. thread 1 thread 2
A B
B
The first thread tries to
acquire lock B. Since it
is busy, it will wait for it.
64. thread 1 thread 2
A B
B A
And the second thread
tries to acquire lock A.
Since it is busy, it will
wait for it.
65. thread 1 thread 2
A B
B A
What did the different
order of lock
acquisition cause?
Keep in mind locks can
be acquired internally
by APIs, by using the
synchronised keyword,
by doing IO. It is almost
impossible to keep
track of all the locks in
a huge application
stack.
DEAD LOCK IS SET.
66.
67.
68. LESSONS
▸ If sharing data between threads, synchronise properly and
measure and stress (same as before).
▸ Keep in mind some dead locks keeps latent and may happen
only in unusual situations (such as unusual high peak load).
▸ The best approach is to minimise sharing data, having
isolated threads working independently.
▸ There are frameworks that suits better than using threads
manually. Consider those, such as Akka, Disruptor, etc.
69. QUESTIONS? THANKS FOR YOUR TIME!
▸ https://www.cs.umd.edu/~pugh/java/memoryModel/
jsr-133-faq.html
▸ http://docs.oracle.com/javase/specs/
▸ fotos: Dani Teston