Exception in thread "Thread-2" java.lang.NullPointerException

Pessoal, tudo bem?
Tive um problema ao executar meu projeto, onde sai a seguinte exceção:

 Exception in thread "Thread-2" java.lang.NullPointerException
       at aula02.Bola.<init>(Bola.java:27)
 	   at aula02.FrmAula02.run(FrmAula02.java:97)
 	   at java.lang.Thread.run(Thread.java:748)

Classe do JFrame:

public class FrmAula02 extends javax.swing.JFrame implements Runnable {

private ArrayList<Bola> listaBolas = new ArrayList<>();    

public FrmAula02() {
    initComponents();
    createBufferStrategy(2);
    Thread t = new Thread(this);
    t.start();

}

/**
 * This method is called from within the constructor to initialize the form.
 * WARNING: Do NOT modify this code. The content of this method is always
 * regenerated by the Form Editor.
 */
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">                          
private void initComponents() {

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGap(0, 400, Short.MAX_VALUE)
    );
    layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGap(0, 300, Short.MAX_VALUE)
    );

    pack();
}// </editor-fold>                        

/**
 * @param args the command line arguments
 */
public static void main(String args[]) {
    /* Set the Nimbus look and feel */
    //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
    /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
     * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
     */
    try {
        for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                javax.swing.UIManager.setLookAndFeel(info.getClassName());
                break;
            }
        }
    } catch (ClassNotFoundException ex) {
        java.util.logging.Logger.getLogger(FrmAula02.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(FrmAula02.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(FrmAula02.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(FrmAula02.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }
    //</editor-fold>

    /* Create and display the form */
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            new FrmAula02().setVisible(true);

        }
    });
}

@Override
public void run() {
    
    Graphics g; 

    for(int i = 0; i < 100; i++)
        listaBolas.add(new Bola(getWidth(), getHeight())); 
    
    g = getBufferStrategy().getDrawGraphics();           
    g.setColor(Color.GREEN);
    g.fillRect(0, 0, getWidth(), getHeight());
    
    listaBolas.forEach((bola) -> {
        bola.drawBall(g);
    });
    
    while(true){
       
       
        listaBolas.forEach((bola) -> {
            bola.moveBall();           
        });

       g.dispose();
       getBufferStrategy().show();
       
       try {
            Thread.sleep(15);
       }
       
       catch (InterruptedException ex) {        
           
       }
       
       
    }    


    
    
}

// Variables declaration - do not modify                     
// End of variables declaration                   

}

Classe Bola:

public class Bola {

private Random r;
private int x, y;
private int dX = 1, dY = 1;
private final int frameWidth;
private final int  frameHeight;

public Bola(int frameWidth, int frameHeight) {
    this.frameWidth = frameWidth;
    this.frameHeight = frameHeight;
    this.x = r.nextInt(frameWidth/2);
    this.y = r.nextInt(frameHeight/2);	

}

public void drawBall(Graphics g) {

    g.setColor(new Color(r.nextInt(255), r.nextInt(255), r.nextInt(255)));
    g.fillOval(x, y, r.nextInt(50), r.nextInt(50));

}

public void moveBall() {

    if(x < 0){
        dX = 1;
    }

    if(x >= frameWidth - 45){
        dX = -1;
    }
      
    if(y < 0){
       dY = 1;
    }
      
    if(y > frameHeight - 45){
       dY = -1;
    }
      
    x += dX;
    y += dY;

}

Onde:

aula02.Bola.<init>(Bola.java:27) faz referência à this.x = r.nextInt(frameWidth/2);
aula02.FrmAula02.run(FrmAula02.java:97) faz referência à listaBolas.add(new Bola(getWidth(), getHeight()));, dentro do laço for
e java.lang.Thread.run(Thread.java:748) faz referência à target.run();

Imagino que a thread considera o ArrayList não sendo totalmente inicializado ou algo do tipo. Alguém poderia dar uma luz?

Agradeço demais!

Não tem nenhum ArrayList na classe Bola.

Você esqueceu de inicializar o objeto r então, quando tenta invocar o nextInt, acontece NullPointerException.