4.8. ThreadsJava makes it easy to define and work with multiple threads of execution within a program. java.lang.Thread is the fundamental thread class in the Java API. There are two ways to define a thread. One is to subclass Thread, override the run() method, and then instantiate your Thread subclass. The other is to define a class that implements the Runnable method (i.e., define a run() method) and then pass an instance of this Runnable object to the Thread() constructor. In either case, the result is a Thread object, where the run() method is the body of the thread. When you call the start() method of the Thread object, the interpreter creates a new thread to execute the run() method. This new thread continues to run until the run() method exits, at which point it ceases to exist. Meanwhile, the original thread continues running itself, starting with the statement following the start() method. The following code demonstrates:
Threads can run at different priority levels. A thread at a given priority level does not run unless there are no higher-priority threads waiting to run. Here is some code you can use when working with thread priorities:
Often, threads are used to perform some kind of repetitive task at a fixed interval. This is particularly true when doing graphical programming that involves animation or similar effects:
Notice the pleaseStop() method in the previous example. You can forcefully terminate a thread by calling its stop() method, but this method has been deprecated because a thread that is forcefully stopped can leave objects it is manipulating in an inconsistent state. If you need a thread that can be stopped, you should define a method such as pleaseStop() that stops the thread in a controlled way. In Java 1.3, the java.util.Timer and java.util.TimerTask classes make it even easier to run repetitive tasks. Here is some code that behaves much like the previous Clock class:
Sometimes one thread needs to stop and wait for another thread to complete. You can accomplish this with the join() method: List list; // A long list of objects to be sorted; initialized elsewhere // Define a thread to sort the list: lower its priority, so it only runs // when the current thread is waiting for I/O, and then start it running. Thread sorter = new BackgroundSorter(list); // Defined earlier sorter.setPriority(Thread.currentThread.getPriority()-1); // Lower priority sorter.start(); // Start sorting // Meanwhile, in this original thread, read data from a file byte[] data = readData(); // Method defined elsewhere // Before we can proceed, we need the list to be fully sorted, so // we've got to wait for the sorter thread to exit, if it hasn't already. sorter.join();
When using multiple threads, you must be very careful if you allow more than one thread to access the same data structure. Consider what would happen if one thread was trying to loop through the elements of a List while another thread was sorting those elements. Preventing this problem is called threadsynchronization and is one of the central problems of multithreaded computing. The basic technique for preventing two threads from accessing the same object at the same time is to require a thread to obtain a lock on the object before the thread can modify it. While any one thread holds the lock, another thread that requests the lock has to wait until the first thread is done and releases the lock. Every Java object has the fundamental ability to provide such a locking capability. The easiest way to keep objects thread-safe is to declare any sensitive methods synchronized. A thread must obtain a lock on an object before it can execute any of its synchronized methods, which means that no other thread can execute any other synchronized method at the same time. (If a static method is declared synchronized, the thread must obtain a lock on the class, and this works in the same manner.) To do finer-grained locking, you can specify synchronized blocks of code that hold a lock on a specified object for a short time:
When you are synchronizing threads, you must be careful to avoid deadlock, which occurs when two threads end up waiting for each other to release a lock they need. Since neither can proceed, neither one can release the lock it holds, and they both stop running:
Sometimes a thread needs to stop running and wait until some kind of event occurs, at which point it is told to continue running. This is done with the wait() and notify() methods. These aren't methods of the Thread class, however; they are methods of Object. Just as every Java object has a lock associated with it, every object can maintain a list of waiting threads. When a thread calls the wait() method of an object, it is added to the list of waiting threads for that object and stops running. When another thread calls the notify() method of the same object, the object wakes up one of the waiting threads and allows it to continue running:
![]() Copyright © 2001 O'Reilly & Associates. All rights reserved. |
|