La barra de progreso es un componente especial ya que su ejecución se enmarca en paralelo a la del resto de elementos. De no ser así y ejecutarse todo en el hilo principal la interfaz se congela debido a la imposibilidad del sistema de otorgar recursos a los componentes

El constructor del componente queda de la siguiente forma:

//Se indica la orientación, el valor máximo y el valor mínimo
JProgressBar barraProgreso = new JProgressBar(SwingConstants.HORIZONTAL,0,100);

Los principales métodos del componente son:

//Asocia la orientación del elemento
barraProgreso.setOrientation(SwingConstants.VERTICAL);
//Obtiene el valor maximo y minomo
barraProgreso.getMaximum();
barraProgreso.getMinimum();
//Modifica el valor máximo y mínimo
barraProgreso.setMaximum(100);
barraProgreso.setMinimum(0);
//Obtiene el valor de la barra de progreso
barraProgreso.getValue();
//Permite mostrar el porcentaje de relleno de la barra de progreso
barraProgreso.setStringPainted(true);
//Pone la barra de progreso sin valores determinado
barraProgreso.setIndeterminate(true)

Manejo de eventos

La barra de progreso se evada mediante un changelistener en el momento que el valor de la barra de progreso es modificado

barraProgreso.addChangeListener(new ChangeListener() {
    @Override
    public void stateChanged(ChangeEvent e) {
        ((JProgressBar)e.getSource()).getValue();
    }
});

Rellenar una barra de progreso

Existen muchas formas de rellenar la barra de progreso. La primera de ellas es en el Thread principal. Esta no es muy recomendable ya que monopoliza todos los recursos

for (int i = barraProgreso.getMinimum(); i<barraProgreso.getMaximum()+1;i++){
    barraProgreso.setValue(i);
    Thread.sleep(100);
}

Este tipo de llenado congelará la interfaz hasta que la barra se llene. Además al tratarse de threads se tratara la excepción InterruptedException

Otro tipo de llenado es mediante un hilo de ejecución. Esta forma es más efectiva ya que crea un nuevo hilo de ejecución paralelo mediante el cual se rellena la barra, no monopolizando así el hilo principal. Para ello se crea un Thread al que se le pasa un objeto Runnable el cual tiene toda la ejecución

b.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent e) {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                for (int i = barraProgreso.getMinimum(); i<barraProgreso.getMaximum()+1;i++){
                    barraProgreso.setValue(i);
                    //System.out.print(Thread.currentThread().getName());
                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }};

        Thread t = new Thread(runnable);
        t.start();

        System.out.print(Thread.currentThread().getName());
    }
});

De esta forma se pueden ejecutar tantos hilos como se quiera sin monopolizar los recursos. El problema está en el tamaño y peso de los mismos

La forma correcta sería mediante un SwingWorker, un objeto muy parecido creado específicamente para trabajar en segundo plano en swing

SwingWorker<Integer,Integer> worker = new SwingWorker() {
                    @Override
                    protected Integer doInBackground() throws Exception {

                        for (int i = barraProgreso2.getMinimum(); i<barraProgreso2.getMaximum()+1;i++){
                            try {
                                Thread.sleep(100);
                                System.out.println(Thread.currentThread());
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }

                            publish(i);
                        }
                        return 100;
                    }

                    @Override
                    protected void process(List chunks) {
                        super.process(chunks);
                        barraProgreso2.setValue((Integer) chunks.get(0));
                    }

                    @Override
                    protected void done() {
                        super.done();
                        getToolkit().beep();
                    }
                };

                worker.execute();