16 August 2001 Simon Gronlund is earning his Master of Science in Computer Science at the Royal Institute of Technology, Sweden, as an adult student. He also teaches Java and computer-related courses at the college. When he isn't tampering with his Warp 4 PC, he spends his spare time with his two boys and his wife. If you have a comment about the content of this article, please feel free to vent in the OS/2 eZine discussion forums. There is also a Printer Friendly version of this page. |
|
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.In short, a single processor (multi processor computers can run many tasks in parallel, but the explanation is still valid) switches from one task to another so rapidly a user does not notice it. If a program is multithreaded the CPU switches from one thread to another one, in one order or another (in fact the switching order and how it is done is up to the implementation of the JRE and may thus differ.) Many programmers are familiar with processes and how to fork a new process. The simplified difference between a process and a thread is that a process has its own working space while a thread is kind of a lightweight process that uses its owners working space. Java uses threads and not processes, except for the main program that builds its own working space. Threads are faster and less resource hungry than processes are. It is common practise when a discussion on multithreading takes off to warn of the side effects. Yes, there are pitfalls. I will not deal too much with them since that is a book in itself, though, I will explain a few later on. Thread classYou will find the class Thread in java.lang . That class implements the Runnable
interface that has only one method to implement, the run method. Let us start with Thread .
There is no point in creating an instance of the Now let us make a small demo.
The constructor takes two parameters, one that will be printed to the standard output and another that is the time to sleep. The method 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.
We instantiate two threads and start them, one with two seconds sleeping time and the other with 1.8 seconds sleeping time. Compile and run and you will soon see the second thread take the lead and lap the first one after a while. 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.
As you can see, the loop will run for one minute and then the exit-line is executed and the program closes, including the two other threads, as they are child-threads from the main app. Note that since Runnable interfaceThe example above works nice, you create your own class extending Thread and that is it. Are there
any drawbacks from that? Yes, if you would like to inherit from another class instead of Thread .
Recall that Java does not support multiple inheritance, which I consider sound and thus Java avoids many nightly
debugging sessions.
Consider you have a little timer ticking somewhere and you found it neat to inherit from We will remake the code above into a GUI app. Let us start with the threads.
This class inherits from Note that we send The run method is somewhat changed, now the These minor changes have turned our class into one that inherits from Now we will change the driver class a little.
The main difference from the former implementation is the lack of the method Since our threads now are 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>. PitfallsMultithreaded applications are prone to severe dangers. Not the tiny demo we made ourselves, but consider more complex tasks. Think of a banking system holding several accounts and yours is $10,000. At a certain time you would like to deposit $500 but by a coincidence the system itself withdraws $1,000 the very same time due to a mortgage payment you have approved earlier. No-one could have foretold that coincidence, and many times it would do no harm but other times you lose $500. What could have happened?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. Thread states
Finally a word on the states
of threads. Naturally they all start at a fresh state directly after 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 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 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. The complete IntoJava archive (updated a few days after a new issue is out.) |
||||||||||||
|