Boa Tarde Pessoal.
Poderiam me ajudar em uma parte do meu código ?
Seguinte.
Eu tenho um banco de dados e quero fazer uma exclusão neste, porém quero por uma barra de progresso enquanto ele faz a exclusão. Logo embaixo eu colocarei o meu código. Eu usei um Thread, porém o tempo que demora para realizar a inclusão é um pouco mais rápido do que a barra. Queria que fosse sincronizado junto com o tempo de exclusão .
Meu código de exclusão é acionado por um botão de excluir. e dentro desse botão eu chamo o método processamento que tem uma thread da barra de progresso.
Segue o código.
private void btn_excluir_pedidoActionPerformed(java.awt.event.ActionEvent evt) {
int resposta = JOptionPane.showConfirmDialog(null,“Tem certeza que desejar exluir este pedido ?”, “Atenção”,JOptionPane.YES_NO_OPTION);
if (resposta == 0) {
Processamento();
new Thread(() -> {
int selecaoNaTabelaPedido = lst_meus_pedidos.getSelectedRow();
int valorIdPedido = Integer.parseInt(lst_meus_pedidos.getValueAt(selecaoNaTabelaPedido, 0).toString());
inserirPedido.excluirPedido(valorIdPedido);
JOptionPane.showMessageDialog(null, "Pedido excluído com sucesso!");
carregarMeusPedidos();
limparCampos();
}).start();
} else {
}
}
Método do processamento.
private void Processamento() {
new Thread(){
@Override
public void run(){
progresso_pedido.setVisible(true);
for (int i = 0; i < 101; i++) {
try {
Thread.sleep(15);
progresso_pedido.setValue(i);
progresso_pedido.setStringPainted(true);
} catch (InterruptedException e) {
JOptionPane.showMessageDialog(null, “Não foi possível o processamento!”);
}
}
progresso_pedido.setVisible(false);
}
}.start();
}
Alguem poderia me ajudar nessa parte ?
Muito obrigado desde já.
Para poder calcular o progresso de uma tarefa, você precisa ter como quebra-la em pedacinhos atômicos. Cada vez que uma tarefa é finalizada, você preenche a barra com numeroDeTarefasConcluidas/numeroTotalDeTarefas.
Nesse caso, a operação de exclusão já é atômica por si só, e não pode ser dividida. Por isso, não tem como você associar diretamente a tarefa de exclusão à barra de progresso dessa forma que eu expliquei.
Porém, você pode por exemplo, iniciar a barra já na metade e preencher até o final assim que a exclusão terminar. Ou então ir preenchendo do começo, bem devagarinho, até a exclusão acabar, e assim terminar de preencher tudo.
De qualquer forma, você precisa necessariamente associar as duas tarefas: o preenchimento da barra e a exclusão. A forma como tu fez ignora completamente a execução da tarefa. Cada thread está fazendo uma coisa completamente independente da outra. Você precisa criar um mecanismo de notificação entre elas.
Fiz um exemplo aqui para te mostrar uma possível forma de fazer a comunicação entre as threads. Pra não precisar implementar nada de blocking manualmente, usei uma BlockingQueue como forma de sincronização. Tecnicamente esse programa ainda está errado e existem janelas de tempo onde ele pode quebrar (nesse caso imprimindo uma informação errada), mas como existe um sleep na tarefa, tudo ocorre normalmente.
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
class BarraDeProgresso {
int atual;
final int total;
BarraDeProgresso(int total) {
this.atual = 0;
this.total = total;
}
synchronized void resetar() {
atual = 0;
}
synchronized float progredir() {
if (atual < total)
atual++;
return getProgresso();
}
synchronized float getProgresso() {
return atual / (float) total;
}
int getTotal() {
return total;
}
@Override
public String toString() {
int atual, total;
synchronized (this) {
atual = this.atual;
total = this.total;
}
StringBuilder result = new StringBuilder('[');
for (int i = 0; i < total; i++) {
if (i < atual)
result.append('+');
else
result.append('_');
}
result.append(']');
return result.toString();
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
int tamanhoDaTarefa = 10;
BarraDeProgresso bdp = new BarraDeProgresso(tamanhoDaTarefa);
BlockingQueue<Object> sincronizador = new LinkedBlockingQueue<>();
Thread threadDaTarefa = new Thread(() -> {
for (int i = 0; i < tamanhoDaTarefa; i++) {
try {
TimeUnit.SECONDS.sleep(1);
bdp.progredir();
sincronizador.put(new Object());
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
});
threadDaTarefa.start();
for (int i = 0; i < tamanhoDaTarefa; i++) {
sincronizador.take();
System.out.println(bdp + " - " + (bdp.getProgresso() * 100) + "%");
}
}
}
Nesse programa, a thread principal fica dormindo. Toda vez que a tarefa avança, a thread principal acorda e imprime o progresso. Poderia ser uma barra de progresso do Swing, por exemplo. Depois, ela volta a dormir e repete.
A thread secundária simula a execução da tarefa, e notifica a thread principal toda vez que acaba uma unidade de trabalho (um sleep nesse caso).
Sim , mas onde entraria a parte da exclusão do banco de dados ? Poderia me dá essa luz ?
Obrigado pelo amparo !
Seria a parte da tarefa. Você vai criar uma unidade de trabalho (um Runnable, por exemplo) e submete-lo para execução fora da thread do Swing (onde o evento de exclusão acontece). Essa unidade de trabalho notifica a thread do Swing de alguma forma (pode ser com SwingUtilities.invokeLater mesmo) assim que a exclusão é feita.
Mais ou menos assim:
void btn_excluir_pedido(evento) {
Runnable tarefaDeExclusao = () -> {
... // faz o trabalho de exclusão
SwingUtilities.invokeLater(() -> {
completaBarraDeprogresso();
});
};
// submete o runnable para execução
new Thread(tarefaDeExclusao).start();
}
Não é muito recomendado ficar criando threads desordenadamente dessa forma, pois é uma operação custosa. A melhor prática é utilizar um pool de threads, que se responsabiliza pela manutenção e reutilização das threads.
Então eu fiz assim:
private void tarefaExlusao(){
int selecaoNaTabelaPedido = lst_meus_pedidos.getSelectedRow();
int valorIdPedido = Integer.parseInt(lst_meus_pedidos.getValueAt(selecaoNaTabelaPedido, 0).toString());
inserirPedido.excluirPedido(valorIdPedido);
JOptionPane.showMessageDialog(null, “Pedido excluído com sucesso!”);
carregarMeusPedidos();
limparCampos();
}
private void btn_excluir_pedidoActionPerformed(java.awt.event.ActionEvent evt) {
int resposta = JOptionPane.showConfirmDialog(null,"Tem certeza que desejar exluir este pedido ?", "Atenção",JOptionPane.YES_NO_OPTION);
if (resposta == 0) {
Runnable tarefaExlusao = () -> {
SwingUtilities.invokeLater(()->{
Processamento();
});
};
new Thread(tarefaExlusao).start();
} else {
}
}
Só que não acontece nada…
Alguma coisa que continuo errando ?
To estudando sobre multi-threading e aconteceu do livro trazer um capítulo exatamente sobre isso: single-threaded gui applications.
Achei interessante a solução que ele propôs. Mudei um pouquinho pra usar Java 8, simplifiquei algumas coisas e trouxe aqui pra você ter uma noção de como é. Os updates de UI tem que ser executados na thread do Swing. Pergunte se não entender algo.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
class CancelationListener implements ActionListener {
Future<?> task;
@Override
public void actionPerformed(ActionEvent e) {
if (task != null)
task.cancel(true);
}
}
public class Main {
static ExecutorService backgroundExec = Executors.newSingleThreadExecutor();
public static void main(String[] args) {
JFrame janela = new JFrame("Application");
JButton btnStart = new JButton("Start");
JButton btnCancel = new JButton("Cancel");
JProgressBar barraDeProgresso = new JProgressBar(0, 4);
btnCancel.setEnabled(false);
janela.setSize(200, 80);
janela.setDefaultCloseOperation(EXIT_ON_CLOSE);
janela.getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER));
janela.add(btnStart);
janela.add(btnCancel);
janela.add(barraDeProgresso);
barraDeProgresso.setEnabled(false);
btnStart.addActionListener(startEvent -> {
btnStart.setEnabled(false);
barraDeProgresso.setEnabled(true);
CancelationListener cl = new CancelationListener();
Runnable cancelationTask = () -> {
SwingUtilities.invokeLater(() -> {
System.out.println("longTask acabou ou foi interrompida.");
// atualiza interface
btnCancel.removeActionListener(cl);
btnCancel.setEnabled(false);
barraDeProgresso.setEnabled(false);
barraDeProgresso.setValue(0);
btnStart.setEnabled(true);
});
};
Runnable longTask = () -> {
for (int i = 1; i <= 4; i++) {
int fi = i;
SwingUtilities.invokeLater(() -> {
barraDeProgresso.setValue(fi);
});
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
cancelationTask.run();
};
cl.task = backgroundExec.submit(longTask);
btnCancel.addActionListener(cl);
btnCancel.setEnabled(true);
});
janela.setVisible(true);
}
}
No livro ele cria uma classe abstrata chamada BackgroundTask<V> que é bem mais elegante do que isso. A ideia é a mesma, só que com a classe abstrata fica mais organizado e dá pra reutilizar. Você consegue adicionar ganchos de progresso e conclusão através de sobrecarga. Ele desenvolve isso porque fala de forma genérica (apesar de usar Swing como exemplo), mas no Swing tem um conceito exatamente igual: SwingWorker.
No JavaFX funciona exatamente da mesma maneira, com o devido Worker.