Do you driver your car with handbreak on? Doesn’t it feel a bit cumbersome?

Well, concurrent programming with java can easily turn into equivalent situation, simply due to fact that some synchronization is required between threads. And sad fact is that there isn’t actually clear workaround for this since correctness of program requires usage of synchronized keyword.

Why usage of synchronized is equivalent into driving with handbreak on? This comes actually due to indirect side-effects of synchronized block. Briefly summarizing, it causes (a) thread may completely block, and thread context switch is done, (b) contents of thread local variables is synchronized with memory.

Issue due to (a) is natural, and often simply mandatory due to correctness. However, issue (b) is more tricky, since it causes nasty behaviour in program. As stated synchronized keyword causes flushing of all changes in member fields back to main memory. And if you think this for a while, it’s clear that it’s bad due to few reasons:

(1) It causes random correctness in program. For example, innocent trace statement in code (which internally has some synchronized block due to correctness), causes actually that program may appear to work correctly, but when innocent trace statement isn’t executed anylonger (ex. logger isn’t having debug level on in actual runtime setup), then program starts to fail mysteriously (and naturally non-reproducible manner, since debugger will also very likely trigger synchronized blocks, hiding the issue) in semi-heavy load.

(2) IT causes that all field values, cached local to thread (like JVM spec states that threads are allowed to do) are flushed back to main memory. Now, this is the ”driving with handbreak on” problem, since every time when synchronized keyword is encountered, then very likely this very important performance optimization fails (i.e. locally cached values are flushed, and re-read from main memory).

Wait a sec, there isn’t anything what can be done about this, since correctness is requiring usage of synchronized. So why all this whining about inevitable. Well, yes, and no. Since there is actually many cases were problem can be avoided, with few off-the-shelf tricks.

Tricks for avoiding turning handbreak on:
(1) Use ThreadLocal
(2) AtomicBoolean, AtomicInteger, etc.
Do you driver your car with handbreak on? Doesn’t it feel a bit cumbersome?

Well, concurrent programming with java can easily turn into equivalent situation, simply due to fact that some synchronization is required between threads. And sad fact is that there isn’t actually clear workaround for this since correctness of program requires usage of synchronized keyword.

Why usage of synchronized is equivalent into driving with handbreak on? This comes actually due to indirect side-effects of synchronized block. Briefly summarizing, it causes (a) thread may completely block, and thread context switch is done, (b) contents of thread local variables is synchronized with memory.

Issue due to (a) is natural, and often simply mandatory due to correctness. However, issue (b) is more tricky, since it causes nasty behaviour in program. As stated synchronized keyword causes flushing of all changes in member fields back to main memory. And if you think this for a while, it’s clear that it’s bad due to few reasons:

(1) It causes random correctness in program. For example, innocent trace statement in code (which internally has some synchronized block due to correctness), causes actually that program may appear to work correctly, but when innocent trace statement isn’t executed anylonger (ex. logger isn’t having debug level on in actual runtime setup), then program starts to fail mysteriously (and naturally non-reproducible manner, since debugger will also very likely trigger synchronized blocks, hiding the issue) in semi-heavy load.

(2) IT causes that all field values, cached local to thread (like JVM spec states that threads are allowed to do) are flushed back to main memory. Now, this is the ”driving with handbreak on” problem, since every time when synchronized keyword is encountered, then very likely this very important performance optimization fails (i.e. locally cached values are flushed, and re-read from main memory).

Wait a sec, there isn’t anything what can be done about this, since correctness is requiring usage of synchronized. So why all this whining about inevitable. Well, yes, and no. Since there is actually many cases were problem can be avoided, with few off-the-shelf tricks.

Tricks for avoiding turning handbreak on:
(1) Use ThreadLocal static final members. Local to thread, so no sync needed.
(2) AtomicBoolean, AtomicInteger, etc. Update doesn’t involve usage of synchronized.
(3) Duplicate elements per thread (no sync at all)
(4) CopyOnWriteArrayList, etc. very nice for avoiding syncs
(5) ConcurrentHashMap is perhaps most important collection, especially ”putIfNeeded” logic
(6) volatile IS very important for simple cases (like ”volatile mAlive”)
(7) Use ReentrantLock, usage of these is just, sadly, a bit clumsy.

Phew, so luckily there is various cases were handbreak can be safely turned off, and all CPU cores can be crunching through data, at full speed. However, all approaches require some special care in correct logistics, since sometimes, synchronized is simply required (so that other threads can see changes in fields).

Update: 4.52011
(8) Make local copy of member field in method. Now, hang-on for a moment, since this is quite important aspect, which applies also to access of singletons and such.

Lets assume, that we have something like this

[code lang=”java”]
class Foo {
private volatile Bar mBar;

// This is called from some bar distributor thread
void changeBar(Bar pBar) {
mBar = pBar;
}

// invoked from bar consumer thread
void doBarUnsafely() {
if (mBar != null) {
mBar.drink();
}
}

// invoked from bar consumer thread
void doBarSafe() {
Bar bar = mBar;
if (bar != null) {
bar.drink();
}
}

}
[/code]

Now difference between doBarUnsafe()and doBarSafe() is very important. In unsafe case, occasional NullPointerException is almost 100% quarantee, with non-reproducible pattern (i.e will happen in production, but never in debugger). While in safe case, NPE will never occur.

Now before, you start saying that proper fix for this would have been to use synchronied, let me say that there is lot of different occasions were pattern like this is actually usefull desired. For example, adding synchronized around drink() might lead into some deadlock, and also would cause blocking in distributor side, since drinking takes too long time. Like it’s not ok that one drinker blocks distributor from closing the bar, while on the other hand it’s perfectly fine from distribution point of view that this specific drinker keeps drinking as long as he desires on that specific bar after closing hour(well, he will eventually leave, so no need to push).

Update: 23.7.2011
Dissecting the Disruptor: Why it’s so fast (part one) – Locks Are Bad

/ java

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *

This site uses Akismet to reduce spam. Learn how your comment data is processed.