Integrar Cópia de arquivos à JProgressBar

Olá pessoal, eu precisava de ajuda para Integrar uma Cópia de arquivos com uma Barra de progresso,
busquei uns exemplos no fórum entendi o funcionamento da JProgressBar os métodos setValue(…), setMaximium(…), etc.
porém quando aplico ao meu código a janela sempre trava. Meu código está ai, alguém poderia modificá-lo integrando a JProgressBar?
Senão for possível apenas me explique o que poderia fazer para conseguir.

Desde já agradeço mesmo, estou postando só pelo fato de não estar conseguindo realizar isso há um bom tempo, sei dos problemas
de posts repetidos, mas é que naqueles que visualizei não encontrei o entendimento da situação. Obrigado a todos e já gostaria de
parabenizar a todos os participantes do GUJ, pelo excelente fórum tirando muitas dúvidas de java.

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JProgressBar;

public class Copy extends JFrame implements Runnable {

    private File caminhoOrigem;
    private File caminhoDestino;
    private JPanel painel;
    private JLabel etiqueta;
    private JProgressBar barraDeProgresso;

    public Copy(File caminhoOrigem, File caminhoDestino) {
        this.painel = new JPanel();
        this.etiqueta = new JLabel(" Copiando arquivos ... ");
        this.barraDeProgresso = new JProgressBar();

        this.caminhoOrigem = caminhoOrigem;
        this.caminhoDestino = caminhoDestino;

        painel.add(etiqueta);
        painel.add(barraDeProgresso);
        this.add(painel);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(200, 100);
        this.setVisible(true);
    }

    public static void copyFile(File origem, File destino) throws IOException {
        if (!destino.exists()) {
            FileInputStream fisOrigem = new FileInputStream(origem);
            FileOutputStream fisDestino = new FileOutputStream(destino);
            FileChannel fcOrigem = fisOrigem.getChannel();
            FileChannel fcDestino = fisDestino.getChannel();
            fcOrigem.transferTo(0, fcOrigem.size(), fcDestino);
            fisOrigem.close();
            fisDestino.close();
        }
    }

    public static void copyAll(File origem, File destino) throws IOException {
        if (!destino.exists()) {
            destino.mkdir();
        }
        
        if (!origem.isDirectory()) {
            throw new UnsupportedOperationException("Origem deve ser um diretório");
        }
        if (!destino.isDirectory()) {
            throw new UnsupportedOperationException("Destino deve ser um diretório");
        }

        File[] files = origem.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                Copy.copyAll(file, new File(destino + "\\" + file.getName()));
            } else {
                System.out.println("Copiando arquivo: " + file.getName());
                Copy.copyFile(file, new File(destino + "\\" + file.getName()));
            }
        }
    }

    public void run() {
        try {
            Copy.copyAll(caminhoOrigem, caminhoDestino);
        } catch (IOException ex) {
            System.err.println("Erro IOException");
        }
    }

    public static void main(String[] args) {
        File arquivo1 = new File("C:\\Teste");
        File arquivo2 = new File("C:\\Temporarios");

        Copy copiar = new Copy(arquivo1, arquivo2);
        copiar.run();
    }
}

Coloque o código da thread separada do componente. Assim a linha de execução do processo que copia vai ser diferente do processo do componente.
Um classe thread CopiaArquivo e um classe BarraDeProgresso.
Do jeito que vc fez está tudo numa thread só. Pense em thread como processo do computador.

[quote=rdgse]Coloque o código da thread separada do componente. Assim a linha de execução do processo que copia vai ser diferente do processo do componente.
Um classe thread CopiaArquivo e um classe BarraDeProgresso.
Do jeito que vc fez está tudo numa thread só. Pense em thread como processo do computador.[/quote]

Obrigado rdgse pelo auxílio, agora teria como demonstrar para mim? O que precisaria implementar nessa classe BarraDeProgresso?

Como eu faço para gerenciar as Threads entre os dois então???

Decidi mudar a visualização para ao invés de aparecer uma barra de progresso o nome do arquivo. Vi esse exemplo na internet de código e mais o menos o que procuro.
Ali seria como um Producer (“Produtor”) gerando código para Consumer (“Consumidor”). Imaginei se aplicar no caso do meu programa para usar cópia de arquivos.

class SharedData {
    int data;
    boolean valueSet = false;

    synchronized void set(int value) {
        if (valueSet) {
            try {
                wait();
            } catch (InterruptedException ex) {
            }
        }
        System.out.println("Generate " + value);
        data = value;
        valueSet = true;
        notify();
    }

    synchronized int get() {
        if (!valueSet) {
            try {
                wait();
            } catch (InterruptedException ex) {
            }
        }
        System.out.println("Get " + data);
        valueSet = false;
        notify();
        return data;
    }
}

class Producer implements Runnable {
    SharedData sd;
    Producer(SharedData sd) {
        this.sd = sd;
        new Thread(this, "Producer").start();
    }

    public void run() {
        sd.set((int) (Math.random() * 100));
    }
}

class Consumer implements Runnable {
    SharedData sd;
    public Consumer(SharedData sd) {
        this.sd = sd;
        new Thread(this, "Cosumer").start();
    }

    public void run() {
        sd.get();
    }
}

public class ProducerConsumerDemo {

    public static void main(String[] args) {
        SharedData sd = new SharedData();
        new Producer(sd);
        new Consumer(sd);
    }
}

Apliquei ao meu código mas ele trava na hora de copiar o arquivo pela segunda vez alguém saberia me explicar??? Só para constar fiz uma espécie de
associação onde Copy é como se fosse Producer, e Frame o Consumer.

package copiararquivos;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.channels.FileChannel;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;

class ArquivoCopiado {

    String nomeArquivo = "";
    boolean valueSet = false;

    synchronized void set(String nomeArquivo) {
        if (valueSet) {
            try {
                wait();
            } catch (InterruptedException ex) {
            }
        }
        System.out.println("Generate " + nomeArquivo);
        this.nomeArquivo = nomeArquivo;
        valueSet = true;
        notify();
    }

    synchronized String get() {
        if (!valueSet) {
            try {
                wait();
            } catch (InterruptedException ex) {
            }
        }
        System.out.println("Get " + nomeArquivo);
        valueSet = false;
        notify();
        return nomeArquivo;
    }
}

class Copy extends Thread {

    private File caminhoOrigem;
    private File caminhoDestino;
    private ArquivoCopiado arquivo;

    Copy(File caminhoOrigem, File caminhoDestino, ArquivoCopiado qtde) {
        super("Copy");
        this.caminhoOrigem = caminhoOrigem;
        this.caminhoDestino = caminhoDestino;
        this.arquivo = qtde;
        this.start();
    }

    synchronized void copyFile(File origem, File destino) throws IOException {
        if (!destino.exists()) {
            FileInputStream fisOrigem = new FileInputStream(origem);
            FileOutputStream fisDestino = new FileOutputStream(destino);
            FileChannel fcOrigem = fisOrigem.getChannel();
            FileChannel fcDestino = fisDestino.getChannel();
            fcOrigem.transferTo(0, fcOrigem.size(), fcDestino);
            fisOrigem.close();
            fisDestino.close();
        }
        arquivo.set(destino.getName());
    }

    synchronized void copyAll(File origem, File destino) throws IOException {
        if (!destino.exists()) {
            destino.mkdir();
        }

        if (!origem.isDirectory()) {
            throw new UnsupportedOperationException("Origem deve ser um diretório");
        }
        if (!destino.isDirectory()) {
            throw new UnsupportedOperationException("Destino deve ser um diretório");
        }

        File[] files = origem.listFiles();
        for (File file : files) {
            if (file.isDirectory()) {
                copyAll(file, new File(destino + "\\" + file.getName()));
            } else {
                System.out.println("Copiando arquivo: " + file.getName());
                copyFile(file, new File(destino + "\\" + file.getName()));
            }
        }
    }

    public void run() {
        try {
            this.copyAll(caminhoOrigem, caminhoDestino);
        } catch (IOException ex) {
            System.err.println("Erro IOException");
        }
    }   
}

class Frame extends JFrame implements Runnable {

    private Thread thread;
    private JPanel painel;
    private JLabel etiqueta;
    private ArquivoCopiado arquivo;

    Frame(ArquivoCopiado arquivo) {
        this.painel = new JPanel();
        this.etiqueta = new JLabel(" Copiando arquivos ... ");
        this.arquivo = arquivo;
        this.thread = new Thread(this, "Frame");
        this.thread.start();
        painel.add(etiqueta);
        this.add(painel);

        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setSize(500, 100);
        this.setVisible(true);
    }

    public void run() {
        etiqueta.setText(arquivo.get());
    }
}

public class Main(){
    public static void main(String[] args) {
        File dir1 = new File("X:\\");
        File dir2 = new File("C:\\Teste");
        ArquivoCopiado arquivoCopiado = new ArquivoCopiado();
        Copy copiar = new Copy(dir1, dir2, arquivoCopiado);
        Frame frame = new Frame(arquivoCopiado);
    }
}

Desde já agradeço.

Ninguém mais ???