1 / 29

Chapter 48

Chapter 48. How to Use Threads to Implement Dynamic Applets.

edan-buck
Download Presentation

Chapter 48

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Chapter 48 How to Use Threads toImplement Dynamic Applets

  2. A process is a running computer program. In a multiprocessing system, the operating system maintains a collection of values, called an execution context, for each process that keeps track of the process's state. Execution contexts allow the operating system to interrupt one process, to run another process for a time, and then to resume the first process.

  3. Each process in a multiprocessing system runs in time slices that are interdigitated with the time slices of other processes, thus sharing the computer's time. If the time slices are short enough, all processes appear to be running simultaneously, although each process appears to run more slowly than it would were it to have all the computer's time to itself.

  4. In an ordinary multiprocessing system, because each process operates in its own private chunk of memory, each program is said to have its own address space.

  5. A running Java program is like a multiprocessing system, because it runs multiple processes that share time. However, a running Java program also is unlike a multiprocessing system, because all the processes share a single address space. • A thread is a process that shares a single address space with other processes. Each thread works independently, sharing time and a single address space with other threads. • Because Java supports threads, Java is said to be multithreaded.

  6. Creating and running your own thread is a three-step process: • You define a subclass of the Thread class. In that definition, you include a definition for a run method. • You create an instance of your subclass of the Thread class. • You call the start method with the instance as the target, whereupon the run method begins to run independently.

  7. Suppose, by way of illustration, that you define the following thread. When run, by calling the start method in main, the thread prints Looping..., as fast as it can, ad nauseam. import java.lang.*; public class DemoThread extends Thread { public static void main (String argv []) { DemoThread thread = new DemoThread(); thread.start(); } public void run () { while (true) { System.out.println("Looping..."); } } } • Note that the Thread class is provided by the java.lang package.

  8. You can slow down a looping thread by adding sleep expressions. They must be in try–catch expressions to handle interruptions. • The following version of DemoThread prints Looping..., every 200 milliseconds, ad nauseam. import java.lang.*; public class DemoThread extends Thread { public static void main (String argv []) { DemoThread thread = new DemoThread(); thread.start(); } public void run () { while (true) { System.out.println("Looping..."); try{sleep(200);} catch (InterruptedException e) {} } } }

  9. Of course, you can substitute any statement you like for the print statement. For example, you can arrange for a thread to increment the position of the text displayed on a component. • Suppose that you decide to define a Marquee class such that instances can display a message, provided via a constructor, at a position dictated by the values of the position and drop instance variables. • The paint method uses the graphics context to display the message in a large bold font. If the value of the ready variable is false, then paint first uses the graphics context to initialize all instance variables, other than message.

  10. import java.awt.*; import javax.swing.*; public class Marquee extends JComponent { private String message; private int position, drop, initialPosition, delta, messageWidth; private Font messageFont = new Font("TimesRoman", Font.BOLD, 24); private boolean ready = false; public Marquee (String s) { message = s; } public void decrementPosition() { if (position + messageWidth < 0) { position = initialPosition; } else { position = position - delta; } repaint(); } public void paint(Graphics g) { // Determine size: Dimension d = getSize(); // Set font g.setFont(messageFont); if (initialPosition != d.width) {ready = false;} if (!ready) { // Set initial position to be the width: position = initialPosition = d.width; // Set the font and determine the message width: FontMetrics f = g.getFontMetrics(); messageWidth = f.stringWidth(message); // Set delta to be equal to the width of the letter e: delta = f.stringWidth("e"); // Set drop so as to center the text vertically: drop = (d.height + f.getHeight() + f.getDescent()) / 2; ready = true; } System.out.println("Painting"); g.drawString(message, position, drop); } public Dimension getMinimumSize() {return new Dimension(300, 50);} public Dimension getPreferredSize() {return new Dimension(300, 50);} }

  11. At first, the message's position is off the display, on the right side. Then, each time that decrementPosition is called, the position shifts left by the value of delta. • Eventually, with sufficient calls, the message shifts entirely to the left of the component, at which point decrementPosition resets the position variable.

  12. By arranging to call decrementPosition at regular intervals, you can ensure that the message will scroll from right to left. • Accordingly, you need to define a subclass of the Thread class—say MarqueeThread—that runs independently, and that calls decrementPosition, with the component as the target, at regular intervals.

  13. So that the run method in the MarqueeThread class has access to the appropriate instance of the Marquee class, that instance of the Marquee class is provided to the thread via the thread's constructor, and is held by the marquee instance variable. • Then, with the Marquee instance available as the value of the marquee instance variable, the run method is readily defined to call decrementPosition periodically. import java.lang.*; public class MarqueeThread extends Thread { private Marquee marquee; public MarqueeThread (Marquee c) { marquee = c; } public void run () { while (true) { // Call to decrementPosition try{sleep(200);} catch (InterruptedException e) {} } } }

  14. You could write the call to decrementPosition straightforwardly, as in the following illustration. Such a call is exceedingly dangerous, and likely to fail in a large application, because decrementPosition may be called while your program's main thread is in the process of executing display operations. Thus, the display operations initiated by your program's main thread and the display operations initiated by the MarqueeThread may step on each other. In such situations, your display goes haywire. import java.lang.*; public class MarqueeThread extends Thread { private Marquee marquee; public MarqueeThread (Marquee c) { marquee = c; } public void run () { while (true) { // Bad programming practice, do not do this! marquee.decrementPosition(); try{sleep(200);} catch (InterruptedException e) {} } } }

  15. The main thread in your program maintains a work queue of display operations. Fortunately, a not-well-known Java mechanism enables you to add your own work-describing class instances to that work queue. That way, your work occurs in between other work on the queue, rather than simultaneously with that other work.

  16. To put work on the display-operations queue, you define a class that implements the Runnable interface, with a run method that calls the decrementPosition method of the Marquee class. If you call that class ChangeHandler, then you call decrementPosition by adding a ChangeHandler instance to the display work queue. import java.util.*; import java.awt.event.*; import javax.swing.*; public class ChangeHandler implements Runnable { private Marquee marquee; public ChangeHandler (Marquee c) { marquee = c; } public void run() { marquee.decrementPosition(); } }

  17. To add to the display work queue a Runnable instance, or an instance of a subclass of Runnable, you call the invokeLater class method of the SwingUtilities class found in the javax.swing package: SwingUtilities.invokeLater(runnable);

  18. Once you understand how to place Runnable instances on the display work queue, you are ready to define MarqueThread properly. import java.lang.*; import javax.swing.*; public class MarqueeThread extends Thread { private Marquee marquee; public MarqueeThread (Marquee c) { marquee = c; } public void run () { while (true) { // Good programming practice; do it this way! ChangeHandler changeHandler = new ChangeHandler(marquee); SwingUtilities.invokeLater(changeHandler); try{sleep(200);} catch (InterruptedException e) {} } } }

  19. You can test the MarqueThread class with the following program: import javax.swing.*; public class ThreadTestor { public static void main (String argv []) { JFrame frame = new JFrame("Thread Test"); Marquee marquee = new Marquee("Buy On To Java Today!"); MarqueeThread thread = new MarqueeThread(marquee); thread.start(); frame.getContentPane().add("Center", marquee); frame.setSize(550, 200); frame.addWindowListener(new ApplicationClosingWindowListener()); frame.show(); } }

  20. At this point, you readily can install a marquee in the evolving movie-rating application. Because none of the other application instances send information to the marquee, you can add the marquee by subclassing the MovieApplication class, placing the marquee in the south slot: import javax.swing.*; import java.awt.event.*; import java.util.*; public class MovieApplicationWithThread extends MovieApplication { // Declare variables private Marquee marquee; private MarqueeThread thread; // Define constructor public MovieApplicationWithThread () { super(); marquee = new Marquee("Buy On To Java Today!"); thread = new MarqueeThread(marquee); thread.start(); getContentPane().add("South", marquee); } }

  21. Once you understand how the Marquee, MarqueeThread, and ChangeHandler classes work together, you can bring all three into one class using locally defined classes. The thread is created, and started, in the constructor.

  22. import java.awt.*; import javax.swing.*; public class Marquee extends JComponent { private MarqueeThread thread; private String message; private int position, drop, initialPosition, delta, messageWidth; private Font messageFont = new Font("TimesRoman", Font.BOLD, 24); private boolean ready = false; public Marquee (String s) { message = s; thread = new MarqueeThread(this); thread.start(); } // Definition of decrementPosition as before // Definition of paint as before class MarqueeThread extends Thread { private Marquee marquee; public MarqueeThread (Marquee c) { marquee = c; } public void run () { while (true) { ChangeHandler changeHandler = new ChangeHandler(marquee); SwingUtilities.invokeLater(changeHandler); try{sleep(200);} catch (InterruptedException e) {} } } } class ChangeHandler implements Runnable { private Marquee marquee; public ChangeHandler (Marquee c) { marquee = c; } public void run() { marquee.decrementPosition(); } } public Dimension getMinimumSize() {return new Dimension(300, 50);} public Dimension getPreferredSize() {return new Dimension(300, 50);} }

  23. Using the definition of the Marquee class shown previously, you can write the following variation on the program: import javax.swing.*; import java.awt.event.*; import java.util.*; public class MovieApplicationWithThread extends MovieApplication { // Declare variables private Marquee marquee; // Define constructor public MovieApplicationWithThread () { super(); marquee = new Marquee("Buy On To Java Today!"); getContentPane().add("South", marquee); } }

  24. To stop a thread, you assign a signaling value to a variable tested inside the run method. In the following, for example, a mouse listener assigns true to the stopper variable, which is tested each time that run loops inside the marquee thread. As soon as stopper is true, run returns. Thus, you can stop the marquee by clicking on it.

  25. import java.awt.*; import java.awt.event.*;import javax.swing.*; public class Marquee extends JComponent { MarqueeThread thread; String message; int position, drop, initialPosition, delta, messageWidth; Font messageFont = new Font("TimesRoman", Font.BOLD, 24); boolean ready = false; boolean stopper = false; public Marquee (String s) { message = s; addMouseListener(new MarqueeListener()); thread = new MarqueeThread(this); thread.start(); } // Definition of decrementPosition as before // Definition of paint as before class MarqueeListener extends MouseAdapter { public void mouseClicked(MouseEvent e) { stopper = true; } } class MarqueeThread extends Thread { private Marquee marquee; public MarqueeThread (Marquee c) { marquee = c; } public void run () { while (true) { if (stopper) {return;} ChangeHandler changeHandler = new ChangeHandler(marquee); SwingUtilities.invokeLater(changeHandler); try{sleep(200);} catch (InterruptedException e) {} } } } class ChangeHandler implements Runnable { private Marquee marquee; public ChangeHandler (Marquee c) { marquee = c; } public void run() { marquee.decrementPosition(); } } public Dimension getMinimumSize() {return new Dimension(300, 50);} public Dimension getPreferredSize() {return new Dimension(300, 50);} }

  26. You can direct a program to wait for a thread to finish its work—that is, for the run method to return—using the join method, as shown in the following example, in which the thread is the value of the thread variable: thread.join(0); • The argument specifies a time interval in milliseconds that your program is to wait. A time interval of zero indicates that you want your program to wait until the thread finishes its work, no matter how long the wait.

  27. You can use the setPriority method to tell Java about the importance of a particular thread in your application. If the thread assigned to thread is not at all important, you evaluate the following statement:thread.setPriority(Thread.MIN_PRIORITY); • On the other hand, if the thread is extremely important, you evaluate the following statement: thread.setPriority(Thread.MAX_PRIORITY);

  28. Multithreaded programs may require synchronization, which is a way of ensuring that two methods will not run at the same time with the same class instance as their target. • One classic example is that of bank-account deposits and withdrawals. If you have different threads call deposit and withdraw methods on the same bank-account instance, there is a chance that both methods will fetch the current balance from an instance variable before either method performs the appropriate addition or subtraction. Thus, one method may work on an out-of-date balance, as illustrated by the following event sequence, producible by two threads running deposit and withdraw methods on the same bank-account instance at the same time: Deposit thread Withdraw thread -------------- --------------- Fetch current balance, 100 Fetch current balance, 100 Add 10 to 100, Write balance, 110 Subtract 10 from 100 Write balance, 90

  29. To solve the interference problem, you need to ensure that the deposit method does not allow the withdraw method to run on the same bank-account instance before the deposit method has finished its work, even though both methods are under the control of independent threads. • To ensure that the two methods do not work on the same bank-account instance at the same time, you need only to mark both method definitions with the synchronized keyword: public synchronized void deposit(int amount) { ... } public synchronized void withdraw(int amount) { ... } • Such synchronized methods cannot run on the same bank-account instance at the same time, because Java has what is called a locking mechanism. Conceptually, each class instance has exactly one lock, and any synchronized method must have that lock to start. Once a synchronized method starts, it holds onto the lock until it has completed its work. Thus, no other synchronized method can run on that class instance during that time.

More Related