title: Why does Java swing timer lead to less animation stutter than sleep?
body: <p>I have two almost identical classes: AnimationFrame1 and AnimationFrame2.  Both of these classes display a blue ball moving back and forth horizontally across a 500 x 500 window.  The two classes are identical save for the runAnimation() and createAndShowGUI() methods.  In its runAnimation() method, AnimationFrame1 uses a while loop and sleep method to create the animation loop whereas AnimationFrame2 uses a Swing Timer.  In its createAndShowGUI() method, AnimationFrame1 creates a new thread and calls the runAnimation() method on it whereas AnimationFrame2 simply calls the runAnimation() method with no new thread.</p>
  
  <p>After compiling both classes, I found that AnimationFrame2, the one that uses the Swing Timer, displays a much smoother animation that doesn't stutter as much as the animation displayed in AnimationFrame1, which uses the while loop and sleep method.  My question is: why does AnimationFrame1 display more stutter in its animation than AnimationFrame2?  I've searched around for a reason for this, but have so far found nothing.</p>
  
  <p>Also, I'm obviously a Java novice, so please let me know if you see anything wrong with my code or if you know of any way I could improve it.</p>
  
  <p>Here is AnimationFrame1: </p>
  
  <pre><code>import javax.swing.*;
  import java.awt.*;
  import java.awt.event.ActionListener;
  import java.awt.event.ActionEvent;
  import java.awt.image.BufferedImage;
  
  class AnimationFrame1 extends JPanel {
  
      int ovalX;
      int prevX;
      Timer timer;
      boolean moveRight;
      BufferedImage img;
  
      public AnimationFrame1() {
          setPreferredSize(new Dimension(500, 500));
      }
  
      public void runAnimation() {
          moveRight = true;
          img = null;
          ovalX = 0;
          prevX = 0;
          while(true) {
              if (moveRight == true) {
                  prevX = ovalX;
                  ovalX = ovalX + 4;
              }
              else {
                  prevX = ovalX - 4;
                  ovalX = ovalX - 4;
              }
              repaint();
              if (ovalX &gt; 430) {
                  moveRight = false;
              }
              if (ovalX == 0) {
                  moveRight = true;
              }
              try {
                  Thread.sleep(25);
              }
              catch(Exception e) {
              }
          }
      }
  
      public void paintComponent(Graphics g) {
          if (img == null) {
              GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
              GraphicsDevice gs = ge.getDefaultScreenDevice();
              GraphicsConfiguration gc = getGraphicsConfiguration();
              img = gc.createCompatibleImage(78, 70);
              Graphics gImg = img.getGraphics();
              gImg.setColor(getBackground());
              gImg.fillRect(0, 0, getWidth(), getHeight());
              gImg.setColor(Color.BLUE);
              gImg.fillOval(4, 0, 70, 70);
              gImg.dispose();
          }
          g.drawImage(img, ovalX, 250, null);
      }
  
      public static void createAndShowGUI() {
          JFrame mainFrame = new JFrame();
          final AnimationFrame1 animFrame = new AnimationFrame1();
          mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          mainFrame.add(animFrame);
          mainFrame.pack();
          mainFrame.createBufferStrategy(2);
          mainFrame.setVisible(true);
          new Thread(new Runnable() {
              public void run() {
                  animFrame.runAnimation();
              }
          }).start();
      }    
  
      public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                  createAndShowGUI();
              }
          });
      }
  
  }
  </code></pre>
  
  <p>And here is AnimationFrame2:</p>
  
  <pre><code>import javax.swing.*;
  import java.awt.*;
  import java.awt.event.ActionListener;
  import java.awt.event.ActionEvent;
  import java.awt.image.BufferedImage;
  
  class AnimationFrame2 extends JPanel {
  
      int ovalX;
      int prevX;
      Timer timer;
      boolean moveRight;
      BufferedImage img;
  
      public AnimationFrame2() {
          setPreferredSize(new Dimension(500, 500));
      }
  
      public void runAnimation() {
          moveRight = true;
          img = null;
          ovalX = 0;
          prevX = 0;
          timer = new Timer(25, new ActionListener() {
              public void actionPerformed(ActionEvent ae) {
                  if (moveRight == true) {
                      prevX = ovalX;
                      ovalX = ovalX + 4;
                  }
                  else {
                      prevX = ovalX - 4;
                      ovalX = ovalX - 4;
                  }
                  repaint();
                  if (ovalX &gt; 430) {
                      moveRight = false;
                  }
                  if (ovalX == 0) {
                      moveRight = true;
                  }
              }
          });
          timer.start();
      }
  
      public void paintComponent(Graphics g) {
          if (img == null) {
              GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
              GraphicsDevice gs = ge.getDefaultScreenDevice();
              GraphicsConfiguration gc = getGraphicsConfiguration();
              img = gc.createCompatibleImage(78, 70);
              Graphics gImg = img.getGraphics();
              gImg.setColor(getBackground());
              gImg.fillRect(0, 0, getWidth(), getHeight());
              gImg.setColor(Color.BLUE);
              gImg.fillOval(4, 0, 70, 70);
              gImg.dispose();
          }
          g.drawImage(img, ovalX, 250, null);
      }
  
      public static void createAndShowGUI() {
          JFrame mainFrame = new JFrame();
          final AnimationFrame2 animFrame = new AnimationFrame2();
          mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
          mainFrame.add(animFrame);
          mainFrame.pack();
          mainFrame.createBufferStrategy(2);
          mainFrame.setVisible(true);
          animFrame.runAnimation();
      }    
  
      public static void main(String[] args) {
          SwingUtilities.invokeLater(new Runnable() {
              public void run() {
                  createAndShowGUI();
              }
          });
      }
  
  }
  </code></pre>