Threading Gotchas in Java
Repeat the mantra with me, Threads are fun. Threads are safe. OK, now
let's list some of the falsehoods, gotchas and little known facts
associated with threading on Java. This page is just a collection of stuff
associated with threads and may not be that connected (but these notes have
proved useful to me).
Each of the following are wrong…
Variable assignment is safe in Java. While assignment to an int
or a boolean
is atomic, assigment to anything else (including double
s
and long
s) are not.
Java threading is OS independent. This is kind of false as well.
Sure, all of the keywords and whatnot are available of every OS, each OS
behaves slightly differently. For instance, NT doesn't have enough priority
levels that Java offers, so if your code assumes that priority 2 is higher
than priority 1, you may be mistaken. This is also an issue if you want
parallelism where two threads are actually running concurrently.
Threads are preemptive. Once again, this is kind of false. On some
JVMs, you could create a thread, but it may either run until conclusion or
not run until the parent thread runs to conclusion. The best approach is to
call sleep()
or block on some IO or something to make sure that other
threads will run.
Synchronization doesn't take any time. While newer JVMs with their
improved threading capabilities have made synchronization execution
shorter, it still takes time. Not only that, but locking out code with the
synchronized
keyword starts to limit the JVMs ability to effectively
manage thread execution.
Sections of Java code have individual locks. Actually, locks (or
mutexes) are associated with an Object. Using the synchronize
keyword
around some code doesn't create a lock for that section. Instead it
acquires the lock associated with the Object instance containing that code. For
instance, if you had two synchronized methods, only one thread can execute
either of those methods.
Whence `stop`?
Once upon a time, I was lulled into thinking that the stop()
method was
safe and fixed when Java first came out. Granted, there are times when it
could be used safely, but its original inclusion was problematic. Look at
the following:
synchronized void myMethod() {
Thread.currentThread().stop();
}
What happens when this method is called? The thread acquires the lock and
then stops… but the lock is never released. The solution is to simply
finish the thread or have the thread abort itself. My personal fav is to
simply have a boolean
value called keepRunning
that the thread
regularly checks to see if it should continue or bail out.
Threads as Timeouts
I recently had a problem where executing a call to URL.getContent()
took
a minute to timeout when the remote host was not running. This was causing
the main program to appear sluggish. My first attempt was to establish a
java.net.Socket
connection and to implement the HTTP protocol, however,
even setting the Socket's initial timeout to 1 millisecond took up to 30
seconds to realize that the remote side was just not going to respond.
Of cource the reason for this is that TCP connections could actually take
some time to
work its way through the Internet to the remote server. However, on a LAN,
this was seen as unacceptable. My solution was to call the getContent
method in a subthread.
The parent thread would then wait()
on the child thread but with a short
timeout, and if the child did not have the data by this point, it could
throw a "timeout" exception and continue on. When the child eventually
timed out, it would end and be cleaned up in the background.
Tell others about this article: