1. Trang chủ
  2. » Công Nghệ Thông Tin

Java Programming for absolute beginner- P21 ppt

20 178 0

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 20
Dung lượng 359,66 KB

Nội dung

Implementing the Runnable Interface There is another way to create a thread. Instead of extending the Thread class, you can implement the Runnable interface. The Runnable interface requires that you override one method. You guessed it: the run() method. To run a Runnable object in a thread, you pass it to the Thread(Runnable) constructor. When you invoke start() on the thread, it results in a call to the Runnable’s run() method. The Thread class, itself, implements the Runnable interface. That’s why when you extend Thread, you implement the run() method. The RunnableTest application is an example of how to implement the Runnable interface to create a thread. Its run() method is exactly the same as the Thread- Test class’s run() method. It just counts to 10. To get this thread started, you need to create a new Thread object (called t) in the main() method This Runnable object is passed into its constructor and then its start() method is invoked. Here is a listing of the source code: /* * RunnableTest * Demonstrates how to implement the Runnable interface */ public class RunnableTest implements Runnable { //must implement the run method public void run() { for (int i=1; i <= 10; i++) { System.out.println(i); } } public static void main(String args[]) { RunnableTest test = new RunnableTest(); //Construct a thread with this Runnable Thread t = new Thread(test); //start the thread t.start(); } } You can see the output of the RunnableTest application in Figure 10.3. What’s the difference between extending Thread and implementing Runnable? Why would you choose one over the other? Generally, it is better to implement the Runnable interface. One good reason for this is that if you are defining a sub- class of another class, such as GUIFrame, you can’t also subclass the Thread class. Inheritance can come from only one direct superclass. To get around this problem, you can extend GUIFrame and implement the Runnable interface too. TRICK HINT 358 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 358 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. 359 C h a p t e r 10 A n i m a t i o n, S o u n d s, a n d T h r e a d s FIGURE 10.3 The output of the RunnableTest application is exactly the same as the ThreadTest application. Problems Associated with Multithreading When two threads are running concurrently, there is no guarantee which thread will be running at any given time. The MultiThreadTest application demon- strates this. It has two threads: the original program’s execution thread and a sec- ond thread, an instance of MultiThreadTest, that the original thread starts. One of the threads lists letters in order and the other lists numbers. The output of this program is not exactly predictable and multiple runs can produce different output. Here is the source code: /* * MultiThreadTest * Demonstrates the unpredictability of multithreading */ public class MultiThreadTest extends Thread { public void run() { for (char a='A'; a <= 'J'; a++) { System.out.println(a); } } public static void main(String args[]) { MultiThreadTest t = new MultiThreadTest(); //fire off the thread t.start(); //continue on simultaneously for (int i=1; i <= 10; i++) { System.out.println(i); } } } You can see the output of the MultiThreadTest application in Figure 10.4. JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 359 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. This is just a simple example, but I’m sure you can imagine what kinds of disas- ters can result if you expect code to be executed in a particular order and you have multiple threads doing different things simultaneously. Your data can become corrupted! Writing Thread-Safe Code Java provides ways to write thread-safe code, that is, code that can have multiple threads executing it simultaneously, and not become unstable because of cor- ruption of some kind. You can use the synchronized keyword as a modifier for methods. This keyword makes sure that only one thread can be executing the method at a time. It works by locking this, the object that owns the code. Only one thread at a time can own a lock for an object. Entering a synchronized method ensures that while a thread is inside it, it has an exclusive lock on this. Any other thread that needs to get into this method has to wait until the lock is released. When a thread passes out of synchronized code, the lock is automati- cally released. public synchronized void mySafeMethod() { … } 360 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r FIGURE 10.4 The MultiThreadTest application ran twice with totally different output. JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 360 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. To enter mySafeMethod(), the thread must gain a lock to the instance of the class that defines the method. When it gets into mySafeMethod(), it automatically gains the lock to this and when it passes out of the method, the lock is released, allowing other threads to enter mySafeMethod(). You can use the synchronized keyword also to lock blocks of code. You can lock on any object: synchronized (myObject) { … } myObject must not be locked by another thread to enter the block of code defined in the curly braces. Any thread that gets into the curly braces gains a lock to myObject. This second method of synchronization is much less commonly used because it can be dangerous to lock any object other than the one that owns the code. volatile is another keyword that has use in multithreaded environments. Only variables can be volatile. This keyword is not used very often. It indicates to the compiler that the variable might be modified out of synch and that multiple threads should be careful when working with it. Using wait(), notify(), and notifyAll() The wait(), notify(), and notifyAll() methods allow for threads to be paused and then restarted. These methods are defined in the Object class. They must be called from within synchronized code. When a thread enters the wait() method, it releases the object lock and pauses execution until another thread invokes the notify() or notifyAll() method. notify() wakes up one thread waiting on this object and notifyAll() notifies all the threads waiting on this object. The wait() method can throw an InterruptedException. A thread can be interrupted while it is waiting or asleep if you invoke the interrupt() method of the Thread class. public synchronized void getValue() { if (valueNotSetYet) { try { wait(); } catch (InterruptedException e) { } } x = value; } public synchronized void setValue() { value = y; valueNotSetYet = false; notify(); } HINT 361 C h a p t e r 10 A n i m a t i o n, S o u n d s, a n d T h r e a d s JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 361 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. In the previous snippet of code, one thread is responsible for getting a value that is set by a different thread. Because you can’t predict exactly when the value will be set, you need to determine whether the value has been set before you try to get it (assume valueNotSetYet is a boolean variable that indicates whether the value has been set). If the value hasn’t been set, you wait for it. Calling wait() releases the lock and allows the second thread to synchronize on this and set the value valueNotSetYet to false, and then call notify() to allow the first thread to retrieve the value it so desperately needs to get. Putting a Thread to Sleep You can pause a thread for a set number of milliseconds by calling the static Thread.sleep(long) method. You will see the importance this feature has for ani- mation when you get to that point to pause between each frame of animation. The long parameter specifies the number of milliseconds the thread sleeps (1000 milliseconds is one second). A sleeping thread can be interrupted, so calling Thread.sleep(long) requires that you handle InterruptedExceptions. Here is an example of an application that calls Thread.sleep(long). It alternatively prints “Tic” and “Toc” to standard output, pausing one second in between each print. Here is the source code for SleepTest.java: /* * SleepTest * Demonstrates how to make a Thread sleep */ public class SleepTest extends Thread { public void run() { for (int i=0; i < 10; i++) { if (i % 2 == 0) System.out.println("Tic"); else System.out.println("Toc"); try { Thread.sleep(1000); } catch (InterruptedException e) {} } } public static void main(String args[]) { SleepTest t = new SleepTest(); t.start(); } } Figure 10.5 shows the output. 362 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 362 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Performing Animation Animation is basically done by showing a series of pictures that differ slightly from each other in a way that makes the image appear to be moving. If the pic- tures change fast enough, the animation looks smooth. In this section, I’ll take you through the process of using threads to animate sprites. The Sprite Class A sprite, in its simplest definition, is any graphical image that moves, especially when in relation to computer games. A sprite is typically made up of a series of images that are used to animate a motion. It also has an (x, y) position and a direction and speed of its movement. The Sprite class I defined for this chapter is a simple class that encapsulates these concepts. It has an array of images, images[], a Point location, location, and another Point, called deltaPoint, which indicates the direction and speed of the sprite. The way this works is the location point is ignored by deltaPoint. deltaPoint indicates the distance the sprite will move in both the x direction and the y direc- tion from where it currently is, wherever that might be, every time the sprite is updated. There is another member, currentImgIndex, which keeps track of the images[] Image element (the actual image) that is set as the current image to be displayed. The Sprite class has its get and set methods for modifying the pro- tected members. The constructor accepts the array of images, the starting loca- tion, and also the deltaPoint Point object. The update() method sets the current image to the next image (if it gets past the last image, it loops back to the first one), and then it moves the current location based on the value of deltaPoint by calling the translate(int, int) method. This method belongs to the Point class and moves the Point object’s location based on the change in x and the change in y that you pass in as its parameters. 363 C h a p t e r 10 A n i m a t i o n, S o u n d s, a n d T h r e a d s FIGURE 10.5 To really benefit from this, you kinda sorta have to run it yourself. JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 363 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. The Sprite class doesn’t perform any animation on its own. It relies on other classes to call its update() method, which creates the illusion of motion. Here is the source listing for Sprite.java: /* * Sprite * Encapsulates images for animation. */ import java.awt.*; public class Sprite { protected Image[] images; protected Point location; //indicates the change in (x, y) protected Point deltaPoint; protected int currentImgIndex; public Sprite(Image[] imgs, Point loc, Point delta) { images = imgs; currentImgIndex = 0; location = new Point(loc.x, loc.y); deltaPoint = new Point(delta.x, delta.y); } public Image getCurrentImage() { return images[currentImgIndex]; } public void setLocation(Point loc) { location = new Point(loc.x, loc.y); } public Point getLocation() { return new Point(location.x, location.y); } public void setDeltaPoint(Point dest) { deltaPoint = new Point(dest.x, dest.y); } public Point getDeltaPoint() { return new Point(deltaPoint.x, deltaPoint.y); } public void update() { currentImgIndex = (currentImgIndex + 1) % images.length; location.translate(deltaPoint.x, deltaPoint.y); } } 364 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 364 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Testing the Sprite Class The SpriteTest class tests the Sprite class by giving it some images, specifying its location, and delta point, and repeatedly calling its update() method from within a thread. Figure 10.6 shows the three images that make up the animation. 365 C h a p t e r 10 A n i m a t i o n, S o u n d s, a n d T h r e a d s FIGURE 10.6 It’s BoitMan! These images are animated by the SpriteTest class. Here is the source code for SpriteTest.java: /* * SpriteTest * Tests the Sprite class */ import java.awt.*; public class SpriteTest extends GUIFrame implements Runnable { Sprite sprite; public SpriteTest(Image[] images, Point loc, Point dest) { super("Sprite Animation Test"); MediaTracker mt = new MediaTracker(this); for (int i=0; i < images.length; i++) { mt.addImage(images[i], i); } try { mt.waitForAll(); } catch (InterruptedException e) {} sprite = new Sprite(images, loc, dest); } public static void main(String args[]) { Image[] imgs = { Toolkit.getDefaultToolkit().getImage("b1.gif"), Toolkit.getDefaultToolkit().getImage("b2.gif"), Toolkit.getDefaultToolkit().getImage("b3.gif") }; SpriteTest spriteTest = new SpriteTest(imgs, new Point(0, 0), new Point(3, 3)); spriteTest.setSize(300, 300); spriteTest.setVisible(true); Thread runner = new Thread(spriteTest); runner.start(); } JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 365 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. //assumes animation moves to the right, down, or both public void run() { while (sprite.getLocation().x < getSize().width && sprite.getLocation().y < getSize().height) { repaint(); sprite.update(); try { Thread.sleep(50); } catch (InterruptedException e) {} } } public void paint(Graphics g) { g.drawImage(sprite.getCurrentImage(), sprite.getLocation().x, sprite.getLocation().y, this); } } It is a GUIFrame that constructs a Sprite object by passing in an array of images consisting of b1.gif, b2.gif, and b3.gif. The second argument to the constructor is new Point(0, 0) so the sprite starts in the top-left corner of the GUIFrame. The third argument is new Point(3, 3), so every time the sprite is updated by call- ing its update() method, it will move three generic units (often pixels) to the right and three down. SpriteTest implements the Runnable interface and overrides run() to move the sprite until it is no longer in the visible GUIFrame area. It calls repaint() to dis- play the image, and then calls sprite.update() to update the sprite to its next frame, sleeps for 50 milliseconds, and then repeats this dance. The paint(Graph- ics) method paints sprite’s image at its current location, which it gets by call- ing sprite.getLocation().x and sprite.getLocation().y. Figure 10.7 hints at what this looks like; however, you have to run it for yourself to actually see the animation. 366 J a v a P r o g r am m i n g f o r t h e A b s o l ut e B e gi n n e r FIGURE 10.7 Go BoitMan, Go! JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 366 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. Double Buffering Did you notice that ugly flickering while BoitMan was running across the screen? This is because the drawing is taking place directly onto the screen. To eliminate the flickering, you need to paint the image to an off-screen buffer first, and then copy the entire off-screen image to the screen. This technique is called double buffering. Here’s how it works. Calling a component’s repaint() method results in a call to the update(Graph- ics) method, which clears the component’s graphics with the background color. You actually see this happening on-screen, which is the main cause of the flick- ering. Then the update(Graphics) method sets the color to the foreground color and invokes paint(Graphics). You see the background color for a split second, and then you see the image again. This is where the flickering comes from. To prevent the flickering, override update(Graphics) so that it doesn’t clear the background. It just calls paint(Graphics) directly. Then from paint(), create an image buffer, which is an invisible image. You create the off-screen image buffer by invoking the createImage(int, int) method that is defined in the Component class. The arguments are its width and its height. Then draw on to the buffered image and then copy the whole thing to the screen. The SpriteNoFlickerTest application performs double-buffering: /* * SpriteTest * Tests the Sprite class */ import java.awt.*; public class SpriteNoFlickerTest extends GUIFrame implements Runnable { Sprite sprite; Image offImg; public SpriteNoFlickerTest(Image[] images, Point loc, Point dest) { super("Sprite Animation Test (No Flicker)"); MediaTracker mt = new MediaTracker(this); for (int i=0; i < images.length; i++) { mt.addImage(images[i], i); } try { mt.waitForAll(); } catch (InterruptedException e) {} sprite = new Sprite(images, loc, dest); } public static void main(String args[]) { Image[] imgs = { Toolkit.getDefaultToolkit().getImage("b1.gif"), Toolkit.getDefaultToolkit().getImage("b2.gif"), Toolkit.getDefaultToolkit().getImage("b3.gif") }; 367 C h a p t e r 10 A n i m a t i o n, S o u n d s, a n d T h r e a d s JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 367 TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark. [...]... other image The result of this effort? No annoying flickering! JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 370 Java Programming for the Absolute Beginner 370 The SoundApplication class imports the java. applet package so it can access the Applet class and the AudioClip class, which are both part of that package It imports the java. net package for the URL class and the MalformedURLException class Calling... code listing for ShootingRange .java: /* * ShootingRange * Animates sprites for the user to try to shoot */ import import import import java. awt.*; java. awt.event.*; java. applet.*; java. net.*; TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 371 371 TEAM LinG - Live, Informative,... targetOnScreen = bulletOnScreen = false; gameOver = true; repaint(); JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 374 Java Programming for the Absolute Beginner 374 //target bounds r2 = new Rectangle(target.getLocation().x, target.getLocation().y, target.getCurrentImage().getWidth(this), target.getCurrentImage().getHeight(this)); return r1.intersects(r2); } / /for keyboard focus public boolean isFocusTraversable()... application */ import import import import java. awt.*; java. awt.event.*; java. applet.*; java. net.*; public class SoundApplication extends GUIFrame implements ActionListener { AudioClip taught; public SoundApplication() { super("Sound Application"); //call Applet's static method to load AudioClip try { taught = Applet.newAudioClip(new URL("file:taught.au")); } catch (MalformedURLException e) {} Button play... on-screen protected boolean bulletOnScreen, targetOnScreen; protected boolean gameOver; protected Image offImg; protected int shots, hits; protected Thread runner; JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 372 Java Programming for the Absolute Beginner 372 addKeyListener(new KeyAdapter() { public void keyPressed(KeyEvent e) { if (e.getKeyCode() == KeyEvent.VK_SPACE && !(bulletOnScreen || gameOver))...JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 368 368 Java Programming for the Absolute Beginner SpriteNoFlickerTest test = new SpriteNoFlickerTest(imgs, new Point(0, 0), new Point(3, 3)); test.setSize(300, 300); test.setVisible(true); Thread... 2/25/03 8:56 AM Page 376 Java Programming for the Absolute Beginner 376 position is built off of the second argument, which specifies the distance between the sprite and the bullet The run() method loops on targetStartDeltaSet[] The total number of aliens is half the length of this array The run() method reuses the same sprite for all the aliens and just updates the point location for each new target The... for animating the bullet and target is the Sprite that animates the alien bulletStartPt is a Point; it specifies where the bullet starts, which is where it is fired (the bottom middle of the screen) Chapter 10 int h = fm.getHeight(); g.setColor(getForeground()); g.drawString(s, (getSize().width - w) / 2, (getSize().height + h) / 2 - fm.getDescent()); JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 376 Java. .. p.y -= distance; return p; } public void run() { gameOver = false; hits = shots = 0; TEAM LinG - Live, Informative, Non-cost and Genuine! Please purchase PDF Split-Merge on www.verypdf.com to remove this watermark JavaProgAbsBeg-10.qxd 2/25/03 8:56 AM Page 373 373 // checks sprites for collision or for going off-screen protected void checkSprites() { Rectangle r, bounds; bounds = new Rectangle(0, 0,... repaints It also updates the bullet if there is one on the screen The checkSprites() method called from the run() method checks for a collision between the bullet and the target It also checks for those sprites moving out of the play area The way it checks for collisions and for being out of the play area is by getting a Rectangle object that represents the Sprite’s bounds and also the Canvas’s bounds . listing for ShootingRange .java: /* * ShootingRange * Animates sprites for the user to try to shoot */ import java. awt.*; import java. awt.event.*; import java. applet.*; import java. net.*; JavaProgAbsBeg-10.qxd. imports the java. applet package so it can access the Applet class and the AudioClip class, which are both part of that package. It imports the java. net package for the URL class and the MalformedURLException class Demonstrates how to play sounds from an application */ import java. awt.*; import java. awt.event.*; import java. applet.*; import java. net.*; public class SoundApplication extends GUIFrame implements

Ngày đăng: 03/07/2014, 05:20