Advanced Programming 2004, based on LY Stefanus’s slides slide 8.1 Multithreading : Thread Scheduling ThreadGroup
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.2 Thread Scheduling On a system with N available processors, we will usually see N of the highest-priority runnable threads executing. Lower-priority threads are guaranteed to run only when higher-priority threads are blocked (not runnable). Lower- priority threads might run at other times to prevent starvation, but we cannot rely on it. A thread is blocked if it is waiting or executing any other system or thread function that is blocked. When a thread blocks, Java picks the highest-priority runnable thread (or one of those with the highest priority if there is more than one thread at that priority) and lets it run. A thread’s priority is initially the same as the priority of the thread that created it. The priority can be changed using the method setPriority with a value between the Thread’s constants MIN_PRIORITY and MAX_PRIORITY. The default priority is NORM_PRIORITY. The method getPriority returns the priority of a thread.
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.3 public static void yield( ) This method causes the currently executing thread to yield so that any other runnable threads can run. The thread scheduler chooses a thread to run from the runnable threads. The thread that is picked can be the one that yielded, because it may be the highest-priority runnable thread. The following example shows how yield works. The program takes a list of words as arguments and creates a thread that is responsible for printing each word.
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.4 public class Babble extends Thread { static boolean doYield; //yield to other threads? static int howOften; //how many times to print? private String word; //word to print Babble( String whatToSay ) { word = whatToSay; } public void run( ) { for (int i=0; i < howOften; i++) { System.out.println(word); if (doYield) yield(); //give another thread a chance }
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.5 public static void main( String[] args ) { doYield = new Boolean(args[0]).booleanValue(); howOften = Integer.parseInt(args[1]); //create a thread for each word at max priority Thread cur = currentThread(); cur.setPriority(Thread.MAX_PRIORITY); for (int i = 2; i< args.length; i++) { new Babble(args[i]).start(); }
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.6 When the threads do not yield, each thread gets large chunks of time, usually enough to finish all the prints without any other thread getting CPU cycles. For example (likely output): >> java Babble false 2 Yes NoWay Yes NoWay >> java Babble true 2 Yes NoWay Yes NoWay Yes NoWay
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.7 User Threads & Daemon Threads Each application starts with one thread – the one that executes main. If the application creates no other threads, it will finish when main returns. But if the application creates other threads, what happens to them when main returns? There are two kinds of threads: user and daemon. The presence of a user thread keeps the application running, whereas a daemon thread is expendable. When the last user thread is finished, any daemon threads are stopped and the application is finished. The status of a new thread is inherited from the thread that creates the new thread and cannot be changed after the new thread is started. Use the method setDaemon(true) to mark a thread as a daemon thread before it is started, and use isDaemon( ) to test that flag.
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.8 If your main method spawns a thread, that thread inherits the user-thread status of the original thread. If you want your application to exit when the original thread dies, you mark all the threads you create as daemon threads. Daemon threads run for benefit of other threads. Garbage collector is a daemon thread.
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.9 volatile If multiple threads could potentially modify a variable, you should mark it as volatile. For example, if you had a value that was continuously displayed by a graphics thread and that could be changed by non-synchronized methods, the display code might look something like this: int currentValue = 123; for (;;) { display.showValue(currentValue); Thread.sleep(1000); } If there is no way for showValue to change the value of currentValue, the compiler might assume that it can treat currentValue as unchanged inside the loop and simply use the constant 123 each time it invokes showValue. But if currentValue is a variable that is updated by other threads while the loop is running, the compiler’s assumption would be wrong.
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.10 Declaring the variable currentValue to be volatile prevents the compiler from making such assumptions, forcing it to reread the value on every iteration of the loop. volatile int currentValue = 123; for (;;) { display.showValue(currentValue); Thread.sleep(1000); }
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.11 Waiting for a Thread to Finish One thread can wait for another thread to finish using the method join. See the Java documentation for details.
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.12 public class ShowJoin { public static void main( String[] args ) { WorkingThread wt = new WorkingThread(); wt.start(); //do something else for (int i=1; i <= 100; i++) System.out.print("*"); try { wt.join(); System.out.println("Result is " + wt.getResult()); } catch (InterruptedException ie) { System.out.println("No result: interrupted"); }
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.13 class WorkingThread extends Thread { private long result; public void run() { result = calculate(40); } public long getResult() { return result; } //calculate the n-th Fibonacci number public long calculate(int n) { if (n == 0 || n == 1) return n; else return calculate(n-1) + calculate(n-2); }
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.14
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.15 Exercise What happens if wt.join( ) is replaced by Thread.sleep(500)?
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.16 Thread Groups Threads are put into thread groups for security reasons. A thread group can be contained within another thread group, providing a hierarchy. Threads within a thread group can modify the other threads in the group, including any threads farther down the hierarchy. A thread cannot modify threads outside its own group. This restriction can be used to protect threads from manipulation by other threads.
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.17 Every thread belongs to a thread group. Each thread group is represented by a ThreadGroup object. We can specify the thread group in the thread constructor; the default is to place each new thread in the same thread group as that of the thread that created it. When a thread dies, the Thread object is removed from its group. See the Java documentation for details.
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.18 Thread groups can be daemon groups. A daemon threadgroup is automatically destroyed when it becomes empty. Setting a threadgroup to be a daemon group does not affect whether any thread or group contained in that group is a daemon or not. It affects only what happens when the group becomes empty. We can use a threadgroup to manage threads in the group.
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.19 Exit Safely The following example interrupts all threads in the current group, lets them finish, and then invokes exit: public static void safeExit( int status ) { //Get the list of all threads Thread myThread = Thread.currentThread(); ThreadGroup thisGroup = myThread.getThreadGroup(); int count = thisGroup.activeCount(); Thread[] threads = new Thread[count + 20]; //+20 for slop thisGroup.enumerate(threads); //Interrupt all threads for (int i = 0; i < threads.length; i++) { if (threads[i] != null && threads[i] != myThread) threads[i].interrupt(); }
Advanced Programming 2004, based on LY Stefanus’s slides slide 8.20 //Wait for all threads to finish for (int i = 0; i < threads.length; i++) { if (threads[i] != null && threads[i] != myThread) { try { threads[i].join(); } catch (InterruptedException ie) { } } //Now we can exit System.exit(status); } Note that when we invoke exit, the JVM will simply stop.