![]() ![]() 16 August 2001 ![]() |
![]() |
Into Java, Part 19
Sweden has shown its best side this summer, the sun shines and the
beach seems to be the proper place to be. However, experiencing such hard circumstances it can be nice to take
refuge in front of the computer to write an article on multithreading. |
||||||||||
We will soon know what multithreading is, how to use threads, and
cover some pitfalls. We will also make use of threads in a tiny demo application.
Threads--what are they?
For a long time computers have had the capability of multitasking,
that is to run several programs in parallel. But for almost as long as that, multithreading has been in use, for
the reason told above. OS/2 supports one of the best schema for both multitasking and multithreading. |
|
The method run overrides the one of the ancestor class Thread.
Since sleep may throw an exception, we must encompass the code with a try/catch block that in fact does nothing
if such an exception is thrown. These exceptions are mainly thrown on running instances from other threads within
the application, but I will not go into that.
The loop will run forever, or at least until the main thread quits
by your action (Ctrl+C). A line is written and then it's time for another nap.
To run this stuff we need a driver class.
|
In fact you have three threads running, the third does nothing
at all. That is the main thread that started the two secondary threads. If this was a GUI app it would likely
listen for user input. But here we can make use of it by adding a few lines to the code directly after the creation
of the second thread shown above.
|
Note that since ThreadDriver does not extend Thread there is no
obvious way to get it to sleep. We then make a call to the static method sleep in the Thread class. Since any
Java program has a thread that is the one initially calling main, this is the way to get to it.
Consider you have a little timer ticking somewhere and you found
it neat to inherit from JLabel. Then you may use the Runnable interface instead. Interfaces are the solid short
cut to multiple inheritance.
We will remake the code above into a GUI app. Let us start with
the threads.
|
Note that we send this as an argument to the Thread constructor.
Otherwise the thread can never know which run method to call upon start. Try to omit this, you will not get any
warnings or errors at all from javac, but nothing will happen to the labels, they will not even show up since
the text is first set in the run method. The thread needs a reference back to the class holding the thread.
The run method is somewhat changed, now the setText method is called
instead of writing to standard output. Otherwise nothing differs from the former implementation.
These minor changes have turned our class into one that inherits
from JLabel and in reality also from Thread, though we had to implement the Runnable interface and create the
thread ourselves.
Now we will change the driver class a little.
|
Since our threads now are JLabel we can add them to the panel that
is the container of the labels. And the constructor of MyThread starts their respective threads, another developer
does not even have to bother with that part of the code.
This time we do not bother to use the main thread, it is waiting
for user input. The only input to get is the exit call, but nevertheless, someone has to do that job too <grin>.
There are a few scenarios possible but I will tell you the one
you do not like. The automatic withdrawal comes first and reads the balance that is $10,000. Then your clerk comes
and his computer reads $10,000, computes the new balance and writes $10,500. Unfortunately the mortgage robot
comes and writes his new balance that is $9,000 and presto, $500 is gone!
Luckily banking systems do not behave this way, they block the
balance until either the robot or the clerk is done and that way such an error could never happen. But think of
a mere multithreaded peer-to-peer chat client where two chatters send their message simultaneously. The messages
can easily get confused by writing a few lines from one and a few lines from the other to the text area.
There are mechanisms prohibiting such errors but I consider it
a little bit off this installment and point you to a splendid paper
by Neel V. Kumar. This paper also deals with deadlocks and livelocks, locks that can block other threads from
doing their tasks. But the part on advanced multithreading is left to you. Remember that concurrent programming
is a large field for research and Java offers most of the technology that field is about.
The lesson to take away is that any time a variable or object can
be set by two or more threads we have to watch out. Another important lesson is that if one object must have information
from another object that in its turn must have information from the first object, information that is not possible
to get until the first object's request is fulfilled, then you have a deadlock. Neither one will be able
to continue since they are waiting for each other's answer.
Livelocks can be the result of a hungry object accepting new tasks
until it runs out of resources. The Internet DOS (Denial of Service) attacks work that way, flood a server with
dummy requests until the server's buffers are overrun and it dies. From a surfer's point of view he is a victim
of Denial Of Service.
Unless nothing happens the thread will reside there, fed by some
CPU cycles from time to time by the operating system and the JRE.
But the developer is in the position of transfer the thread to
the "blocked" state where it will not ask for more CPU cycles, it is really asleep. Any use of sleep
will cause that. But also any call to I/O methods, such as readLine, will make the thread blocked until an end-of-line
character is found. Hence you must not put any thread to such work, especially not the one listening for user
input.
A call to stop causes the thread to die, if you do not watch out,
nothing will be saved. The natural way is to let the thread die by itself when it has finished the run method
and leaves it. A thread will also die if the main thread exits.
Please remember that the thread of an object does not deliver work
to another thread by itself. That is, if object A (that has its own thread) makes a call to a method of object
B (that also has its thread), it is not the B-thread that continues the work. It is the A-thread that visits object
B and does something within B's methods and with its variables using the methods.
Many beginners think that B can do some internal heavy work while
A will continue listen to a stream for example. That is not the case! If the streamlistener must be responsive,
you must hand over the input to B but let B do the work with lower priority, and--hey!--multithreading is not
that simple.
Next time we will make use of these new concepts and look into
networking. Stay tuned.
Previous Article |
|
Next Article |