📄 ch8.htm
字号:
<P>It is easy to modify the example to have multiple line-drawingthreads. If you add the following code, there will be two threadsdrawing lines on the applet:<BLOCKQUOTE><TT>Thread t2 = new LineThread(this);<BR>t2.start();</TT></BLOCKQUOTE><P>You should add corresponding <TT>stop()</TT>and <TT>join()</TT> methods to terminatethe threads in the applet <TT>destroy()</TT>method.<H2><A NAME="TheRunnableInterface"><FONT SIZE=5 COLOR=#FF0000>TheRunnable Interface</FONT></A></H2><P>One thing may strike you as troubling in this discussion abouthow threads work. If you have to use the Thread class every timeyou need to run something as a thread, don't you lose the capabilityto inherit other classes? Because Java is a single-inheritancesystem, forcing a class to inherit the Thread class would seemto be overly restrictive because you couldn't, for example, runthe Applet class as a thread. Fortunately, Java actually implementsthreads through an interface, thus allowing any class to use athread's functions. The interface is called <I>Runnable</I> andhas only one method, <TT>run()</TT>.The <TT>run()</TT> method of the Threadclass is itself an implementation of Runnable.<P>The typical way of starting a class that implements Runnable asa thread is to call an alternate Thread constructor. This constructortakes a Runnable class as its target parameter. For example, ifyou have a Runnable object assigned to the variable <TT>r</TT>,you would pass it to the Thread constructor as follows:<BLOCKQUOTE><TT>t = new Thread(r);</TT></BLOCKQUOTE><P>When the thread's <TT>start()</TT>method is called after this, the <TT>run()</TT>method of the Runnable target is invoked. If you don't want toexplicitly declare a <TT>Thread</TT>variable, you can start the Runnable target in a more concisemanner:<BLOCKQUOTE><TT>new Thread(r).start();</TT></BLOCKQUOTE><P>A Thread object created with a Runnable target behaves similarlyto the thread behavior discussed in the previous sections. The<TT>stop()</TT> method terminatesthe thread, <TT>suspend()</TT> and<TT>resume()</TT> are used to pauseand restart the thread, and so on.<P>The line-drawing applet can be reworked to use the Runnable interface.In the example shown in Listing 8.3, the LineThread class is removed,and the Applet class (called LineRun, in this case) implementsthe Runnable interface. Therefore, there is only one class inthe applet that both draws the lines and runs as a thread.<HR><BLOCKQUOTE><B>Listing 8.3. The line-drawing applet using the Runnable interface.<BR></B></BLOCKQUOTE><BLOCKQUOTE><TT>import java.awt.*;<BR>import java.lang.*;<BR>import java.applet.Applet;<BR><BR>// Runs the Applet as a Thread so that it can paint<BR>// lines indefinitely...<BR>public class LineRun extends Applet implements Runnable {<BR> Thread t; // This is the thread!<BR> // Set the size of the applet...<BR> public void init() {<BR> resize(300,300);<BR> }<BR><BR> // Entering the Applet. Start the thread...<BR> public void start() {<BR> if (t == null) {<BR> t = new Thread(this);<BR> t.start();<BR> } // end if<BR> }<BR><BR> // Leaving the Applet. Stop the thread...<BR> public void stop() {<BR> if (t != null) {<BR> t.stop();<BR> try {<BR> t.join();<BR> }<BR> catch (InterruptedExceptione) { }<BR> } // end if<BR> t = null;<BR> }<BR><BR> // Run the thread. Lines everywhere!<BR> public void run() {<BR> // Get dimension data aboutthe applet...<BR> double width = (double)size().width;<BR> double height = (double)size().height;<BR> // Loop and draw lines forever...<BR> while (true) {<BR> Graphicsg = getGraphics();<BR> // Randomlyselect a color...<BR> Color c= new Color((int)(255.0 * Math.random()),<BR> (int)(255.0* Math.random()),<BR> (int)(255.0* Math.random()) );<BR> g.setColor(c);<BR> g.drawLine((int)(width* Math.random()),<BR> (int)(height* Math.random()),<BR> (int)(width* Math.random()),<BR> (int)(height* Math.random()) );<BR> // Sleepsome...<BR> try {<BR> t.sleep(100);<BR> }<BR> catch (InterruptedExceptione) { }<BR> }<BR> }<BR>}</TT></BLOCKQUOTE><HR><P>There are a couple of interesting things to note about this applet.It looks almost exactly like the example in the previous section.In fact, the <TT>run()</TT> methodof Listing 8.3 is only slightly modified from that of the secondversion of the LineThread class; the only changes are the removalof the references to the Applet class and how the <TT>sleep()</TT>method is invoked.<P>The thread that runs the Runnable target is constructed in the<TT>start()</TT> method of the LineRunclass. (Keep in mind that the <TT>start()</TT>method here is that of the Applet class and not the Thread class.)You must check whether the thread already exists because you mightmove back and forth from the line-drawing Web page to anotherpage. If you leave the drawing page, the Applet <TT>stop()</TT>method kills the thread and waits for it to die. It will be restartedif you return to the page.<P>An interesting thing to note about classes that implement Runnableis that they are not "dead" if their <TT>run()</TT>method has been stopped. Even if the thread is dead, the othermethods in a class using Runnable still can be invoked. For example,add the following code to the LineRun class:<BLOCKQUOTE><TT>// Toggle the running of the line-drawingthread...<BR>public boolean mouseDown(Event ev, int x, int y) {<BR> // Stop the thread if it is running...<BR> if (t != null) {<BR> t.stop();<BR> try {<BR> t.join();<BR> }<BR> catch (InterruptedExceptione) { }<BR> t = null;<BR> }<BR> // If it is dead, then start a newthread...<BR> else {<BR> t = newThread(this);<BR> t.start();<BR> }<BR> return true;<BR>};</TT></BLOCKQUOTE><P>This code stops the thread if it is running and restarts it ifthe thread is stopped. Therefore, the LineRun object is aliveregardless of whether the <TT>run()</TT>method is active. This example points out an advantage of usingthe Runnable interface over creating a subclass of Thread. Whena Thread object's <TT>run()</TT> methodis stopped, it cannot be restarted; you have to create a new Threadto have its <TT>run()</TT> methodrun again. On the other hand, an object using Runnable can haveits <TT>run()</TT> method repeatedlystarted and stopped.<P>Another advantage of using the Runnable interface for your customthread is that it does carry all the baggage of the Thread class,which supports over two dozen methods. It seems wasteful to subclassthreads so that you can override only the <TT>run()</TT>method. On the other hand, the Thread class might be useful ifyour threaded class doesn't need to be a subclass of a complexclass like Applet. In general, it's best to follow the designversus implementation rule: If your class is designed to be aspecial kind of thread, then use the Thread class; if it's designedto be something else (like an AWT component) but needs to implementthreads, then use Runnable.<H2><A NAME="Synchronization"><FONT SIZE=5 COLOR=#FF0000>Synchronization</FONT></A></H2><P>As you can see from the previous examples, writing threaded classesin Java is generally pretty easy, so you might wonder why, untilrecently, relatively few languages and programs have supportedmultithreading. The reason for this is that multithreading isnot without risks. In particular, a class meant to run in a concurrentenvironment must be designed so that multiple threads can accessan instance of the class without causing undesired behavior. Aninstance of the class must be as reliable in an environment withmultiple threads of execution as it is within a single-threadedenvironment. In other words, the class must be <I>thread-safe</I>.You will now see an example of a class that's not thread-safe.<H3><A NAME="ATestStackClassThatIsNotThreadSaf">A TestStack ClassThat Is Not Thread-Safe</A></H3><P>Listing 8.4 shows the code for a class that is a stack of Integerobjects. It is a generally well-constructed class, showing goodJava programming practices, such as handling errors (like an emptystack) by throwing exceptions. However, it is not thread-safe.Why is this? Look at the <TT>push()</TT>and <TT>pop()</TT> methods. They bothuse the <TT>top</TT> integer variableto indicate the current top of the stack. But what happens ifone thread is calling <TT>push()</TT>at the same time another thread is calling <TT>pop()</TT>?In this situation, <TT>push()</TT>and <TT>pop()</TT> might behave inan undesired fashion.<HR><BLOCKQUOTE><B>Listing 8.4. A TestStack class that isn't thread-safe.<BR></B></BLOCKQUOTE><BLOCKQUOTE><TT>// Classes of stack exceptions...<BR>class StackException extends Exception { }<BR>class StackFullException extends StackException { }<BR>class StackEmptyException extends StackException { }<BR><BR>// This class implements a simple stack as<BR>// an array of integers...<BR>class TestStack {<BR> Integer s[]; // The stack array ofinteger objects...<BR> int top; // The current top of the stack. Nextitem to place.<BR> // Construct a stack of the specified size...<BR> public TestStack(int size) {<BR> s = new Integer[size];<BR> top = -1; //Empty stack...<BR> }<BR> // Push an item onto the stack...<BR> public void push(Integer item) throws StackFullException{<BR> // Throw exception if stackis full...<BR> if (top == s.length)<BR> thrownew StackFullException();<BR> // Otherwise increment thetop and add the item...<BR> ++top;<BR> s[top] = item;<BR> }<BR><BR> // Pop the top item off the stack...<BR> public Integer pop() throws StackEmptyException{<BR> // Throw exception if stackis empty...<BR> if (top < 0)<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -