View Full Version : [Java] Thread
Ho un problema....
Quando creo una classe estesa da Thread ( una balla di caricamento ), non riesco a farla scorrere prima del termine dell'operazione.
Ho provato a mettere l'operazione che faccio successivamente come un Thread ma nemmeno questo influsice, la barra appare ma come se fosse bloccata. Appena il progesso finisce, si sblocca e si muove.
Per essere piu preciso:
La classe per il caricamento :
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
/**
*
* @author c.bianchini
*/
public class Caricamento extends Thread {
private javax.swing.JLabel jLabel1;
private javax.swing.JProgressBar jProgressBar1;
private JFrame frame;
private Thread t;
private String testo,testo2;
/** Creates a new instance of Caricamento */
public Caricamento(int g,String testo,String testo2) {
this.testo = testo;
this.testo2 = testo2;
// t = new Thread(this);
}
public void SetTesto(String testo) {
jLabel1.setText(testo);
}
public void Invisibile() {
frame.setVisible(false);
}
public void Visibile() {
frame.setVisible(true);
}
public void run() {
System.out.println("aaaa");
jProgressBar1 = new javax.swing.JProgressBar();
jLabel1 = new javax.swing.JLabel();
frame = new JFrame(testo);
frame.setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
frame.setResizable(false);
jProgressBar1.setIndeterminate(true);
jLabel1.setText(testo2);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(frame.getContentPane());
frame.getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jLabel1)
.addContainerGap(356, Short.MAX_VALUE))
.addComponent(jProgressBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jProgressBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel1))
);
frame.pack();
frame.setVisible(true);
}
}
Poi il codice nel main:
Caricamento load2 = new Caricamento(100,"Sto applicando le ACL...","");
load2.start();
ACL a = new ACL();
Quando uso la classe ACL per fare delle operazioni... la barra di caricamento appare senza che scorre ma appna la classe finisce il suo lavoro , rivive.
L'EDT è il Thread che Swing usa per disegnare i componenti e gestire gli eventi. Parte e lavora automaticamente.
Il sintomo che descrivi è quello di un sovraccarico di questo EDT.
L'apertura di una finestra richiede che l'EDT esegua una successione di compiti. Se tra l'inizio e la fine di questa successione infilassimo la richiesta di esecuzione di un compito oneroso, allora potremmo vedere una finestra non pienamente funzionante.
A prescindere da questo il codice che proponi nasconde una precondizione che contraddittoria.
Vale a dire che pur essendo "Caricamento" un Thread, l'invocazione del suo metodo start() è sempre un errore logico.
L'errore sta in ciò che i due enunciati contenuti nel metodo run() di Caricamento:
frame.pack();
frame.setVisible(true);
devono obbligatoriamente essere eseguite dall'EDT. L'invocazione di start() su un Caricamento ne causa invece sempre ed inevitabilmente l'esecuzione in un Thread diverso dall'EDT.
Lo stesso vale per i metodi visible(), invisible() e setTesto(String) (il bon ton Javesco vuole che i metodi inizino sempre con una lettera minuscola).
Una possibile soluzione "parallelamente corretta" del problema è facile ma articolata. Richiede la presenza di un Thread diverso dall'EDT che blocca il flusso di esecuzione quando incontri una richiesta "apri la finestra di caricamento" e rilasci tale blocco quando la finestra risulti pienamente visibile. Lo stesso dovranno fare i metodi visible() e invisible() mentre setTesto(String) può limitarsi ad affidare gli enunciati ora direttamenti all'esecuzione indiretta tramite EDT.
L'EDT è il Thread che Swing usa per disegnare i componenti e gestire gli eventi. Parte e lavora automaticamente.
Il sintomo che descrivi è quello di un sovraccarico di questo EDT.
L'apertura di una finestra richiede che l'EDT esegua una successione di compiti. Se tra l'inizio e la fine di questa successione infilassimo la richiesta di esecuzione di un compito oneroso, allora potremmo vedere una finestra non pienamente funzionante.
A prescindere da questo il codice che proponi nasconde una precondizione che contraddittoria.
Vale a dire che pur essendo "Caricamento" un Thread, l'invocazione del suo metodo start() è sempre un errore logico.
L'errore sta in ciò che i due enunciati contenuti nel metodo run() di Caricamento:
frame.pack();
frame.setVisible(true);
devono obbligatoriamente essere eseguite dall'EDT. L'invocazione di start() su un Caricamento ne causa invece sempre ed inevitabilmente l'esecuzione in un Thread diverso dall'EDT.
Lo stesso vale per i metodi visible(), invisible() e setTesto(String) (il bon ton Javesco vuole che i metodi inizino sempre con una lettera minuscola).
Una possibile soluzione "parallelamente corretta" del problema è facile ma articolata. Richiede la presenza di un Thread diverso dall'EDT che blocca il flusso di esecuzione quando incontri una richiesta "apri la finestra di caricamento" e rilasci tale blocco quando la finestra risulti pienamente visibile. Lo stesso dovranno fare i metodi visible() e invisible() mentre setTesto(String) può limitarsi ad affidare gli enunciati ora direttamenti all'esecuzione indiretta tramite EDT.
Un esempio.. un aiuto... so che Swing usa un suo thread suo.. ma non riesco a trovare la soluzione leggendo questa dettagliatissima spiegazione :D
Vediamo prima una versione breve. Se il compito da eseguire è apprezzabilmente lungo ed è affidato ad un Thread diverso dall'EDT allora basta e avanza.
Creo prima un'interfaccia per rendere chiaro quale sia l'insieme di capacità di questa finestra di progresso, a prescindere da come siano portati avanti i lavori.
package workinprogress;
/** Questa è la dichiarazione di ciò che farà la finestra di dialogo con
la barra di avanzamento. */
public interface ProgressDialog {
/** Imposta il testo proiettato nella finestra. */
void setTesto(String testo);
/** Chiude la finestra di avanzamento. */
void invisible();
/** Apre la finestra di avanzamento. */
void visible();
}
Anticipo l'uso della classe che concretizzerà ProgressDialog, così vedi se è quello che ti serve.
package workinprogress;
public class Main {
public static void main(String[] args) {
//questi enunciati sono eseguiti dal Thread "Main"
ProgressDialog dialog = new SwingProgressDialog();
try {
dialog.visible(); //apre la finestra
dialog.setTesto("Avvio..."); //primo messaggio
sleep(1000); //simula un passaggio del compito eseguito dal Thread diverso dall'EDT
dialog.setTesto("Proseguimento..."); //messaggio
sleep(1000); //passo simulato
dialog.setTesto("Fine..."); //messaggio
sleep(1000); //passo simulato
} finally {
dialog.invisible(); //chiude la finestra
}
}
private static void sleep(long ms) {
try {
Thread.sleep(ms);
} catch(InterruptedException ex) {
ex.printStackTrace();
}
}
}
SwingProgressDialog fa quello che faceva la tua classe ma affida l'esecuzione dei compiti di proiezione e aggiornamento dei componenti all'EDT.
Per farlo esiste un meccanismo ad hoc. Prendi gli enunciati che interagiscono con un componente Swing proiettabile e li metti nel metodo run di un Runnable. Poi passi quel Runnable al metodo invokeLater di EventQueue. Cioè se vuoi invocare:
frame.pack();
frame.setVisible(true);
scrivi:
Runnable task = new Runnable() {
public void run() {
frame.pack();
frame.setVisible(true);
}
};
EventQueue.invokeLater(task);
A questo punto "frame.pack()" e "frame.setVisible(true)" saranno eseguiti dall'EDT. Uso il futuro perchè non siamo gli unici a voler far fare qualcosa all'EDT. Può darsi che prima del nostro "task" deva fare qualcos'altro. Comunque sono operazioni rapide per cui quando "invokeLater" sai che il Runnable sarà eseguito quasi istantaneamente.
Detto questo, la versione concreta di ProgressDialog è facilmente immaginabile.
package workinprogress;
import java.awt.*;
import javax.swing.*;
/** Questo è come lo fa. */
public class SwingProgressDialog implements ProgressDialog {
private JLabel textLabel = new JLabel();
private JFrame window = new JFrame("Work in progress.");
private JProgressBar progressBar = new JProgressBar();
public SwingProgressDialog() {
window.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
textLabel.setPreferredSize(new Dimension(300, 150));
textLabel.setHorizontalAlignment(JLabel.CENTER);
progressBar.setIndeterminate(true);
JPanel container = new JPanel(new BorderLayout(8, 8));
container.add(textLabel, BorderLayout.CENTER);
container.add(progressBar, BorderLayout.SOUTH);
container.setBorder(BorderFactory.createEmptyBorder(8, 8, 8, 8));
window.add(container);
}
public void visible() {
Runnable edtTask = new Runnable() {
public void run() {
window.pack();
window.setVisible(true);
}
};
EventQueue.invokeLater(edtTask);
}
public void invisible() {
Runnable edtTask = new Runnable() {
public void run() {
window.setVisible(false);
window.dispose();
}
};
EventQueue.invokeLater(edtTask);
}
public void setTesto(final String text) {
Runnable edtTask = new Runnable() {
public void run() {
textLabel.setText(text);
}
};
EventQueue.invokeLater(edtTask);
}
}
A questo punto con uno SwingProgressDialog:
SwingProgressDialog x = new SwingProgressDialog();
puoi invocare i metodi visible, invisible e setTesto da un Thread diverso dell'AWT Event Dispatcher (sempre l'EDT, nome proprio) nel pieno rispetto di Swing e del risultato voluto.
L'invocazione degli stessi metodi da parte dell'EDT può invece causare risultati inattesi. In particolare, può non apparire un bel niente, le cose possono non essere aggiornate come dovrebbero eccetera. Ciò capita se al normale compito di disegno affianchi procedimenti che siano onerosi in termini di tempo o di risorse di calcolo. E' una cosa che andrebbe comunque evitata per cui la più complessa "soluzione B" non dovrebbe essere necessaria.
Compiling 1 source file to C:\Documents and Settings\xxx\ACL\build\classes
C:\Documents and Settings\xxx\ACL\src\acl\Caricamento.java:66: cannot find symbol
symbol: class ProgressDialog
public class Caricamento implements ProgressDialog {
1 error
BUILD FAILED (total time: 0 seconds)
La classe Progress Dialog è una standard o devo cercarla su internet e implementarla?
E' contenuta nei sorgenti che ho incollato (il primo, è un'interfaccia) ma l'ho messa solo per evidenziare il contratto. Tu puoi anche togliere quell' "implements ProgressDialog" e usare direttamente SwingProgressDialog.
Poi probabilmente userai delle classi tue, questo è solo un esempio del modo.
Niente... funziona allo start del programma ma quando avvio la procedura di eseguire delle operazione è come prima:
public class Caricamento {
private javax.swing.JLabel jLabel1;
private javax.swing.JProgressBar jProgressBar1;
private JFrame frame;
private String testo,testo2;
public Caricamento(String testo, String testo2) {
this.testo = testo;
this.testo2 = testo2;
jProgressBar1 = new javax.swing.JProgressBar();
jLabel1 = new javax.swing.JLabel();
frame = new JFrame(testo);
frame.setDefaultCloseOperation(javax.swing.WindowConstants.DO_NOTHING_ON_CLOSE);
frame.setResizable(false);
jProgressBar1.setIndeterminate(true);
jLabel1.setText(testo2);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(frame.getContentPane());
frame.getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jLabel1)
.addContainerGap(356, Short.MAX_VALUE))
.addComponent(jProgressBar1, javax.swing.GroupLayout.DEFAULT_SIZE, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jProgressBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 19, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jLabel1))
);
frame.pack();
frame.setVisible(true);
}
public void SetTesto(final String testo) {
Runnable edtTask = new Runnable() {
public void run() {
jLabel1.setText(testo);
}
};
EventQueue.invokeLater(edtTask);
}
public void Invisibile() {
Runnable edtTask = new Runnable() {
public void run() {
frame.setVisible(false);
frame.dispose();
}
};
EventQueue.invokeLater(edtTask);
}
public void Visibile() {
Runnable edtTask = new Runnable() {
public void run() {
frame.pack();
frame.setVisible(true);
}
};
EventQueue.invokeLater(edtTask);
}
}
Prima di lanciare la "procedura" scrivi:
System.out.println(Thread.currentThread());
Verifica che il Thread che esegue la procedura non sia l'EDT. (AWT Event Dispatcher). Se sì, allora devi affidare l'esecuzione della procedura ad un Thread diverso.
Prima di lanciare la "procedura" scrivi:
System.out.println(Thread.currentThread());
Verifica che il Thread che esegue la procedura non sia l'EDT. (AWT Event Dispatcher). Se sì, allora devi affidare l'esecuzione della procedura ad un Thread diverso.
Ok verificato e mi da : Thread[AWT-EventQueue-0,6,main]
Scusa ma non sono molto afferato con sti Thread, mi dai una mano a
"Se sì, allora devi affidare l'esecuzione della procedura ad un Thread diverso."
Grazie :D
Diciamo che ACL è un oggetto che possiede i metodi necessari all'esecuzione dell'operazione.
Ora hai una cosa di questo genere:
Caricamento car = new Caricamento();
ACL acl = new ACL();
try {
car.visible();
acl.computazioneUno();
car.setTesto("blabla");
acl.computazioneDue();
car.setTesto("blibli");
finally {
car.invisible();
}
Probabilmente 'sta cosa è contenuta in un actionPerformed, il che ci dice perchè il thread corrente è l'EDT.
Bene, la soluzione sta nell'affidare l'esecuzione delle computazioni ad un Thread ad hoc. Caricamento, per via di quella ridda di EventQueue.invokeLater, è perfettamente in grado di operare in un contesto concorrente (2 thread, l'EDT e il tuo).
La stessa cosa di prima ora sarà:
new Thread() { public void run() {
Caricamento caricamento = new Caricamento();
ACL acl = new ACL();
try {
caricamento.visible();
acl.computazione();
caricamento.setText("blabla");
acl.computazione();
caricamento.setText("blibli");
acl.computazione();
} finally {
caricamento.invisible();
}
}}.start();
A questo punto le computazioni non pesano più sul Thread che si occupa di visualizzare ed aggiornare in componenti ma su un altro Thread. Questo dovrebbe consentire all'EDT di mostrare l'interfaccia Caricamento in tutto il suo splendore.
Calma e gesso.
A seconda del tipo di applicazione, potresti incontrare altri problemi nell'uso di quel componente Caricamento. In particolare, la finestra che si apre non è modale, quindi non impedisce all'utente di premere un'altra volta il pulsante nè rimane in primo piano rispetto al resto dell'applicazione.
Insomma, non è ancora detto che quella sia anche la soluzione che ti serve. Devi vedere un po' il programma cosa richiede.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.