Torna indietro   Hardware Upgrade Forum > Software > Programmazione

Roborock Qrevo Curv 2 Flow: ora lava con un rullo
Roborock Qrevo Curv 2 Flow: ora lava con un rullo
Qrevo Curv 2 Flow è l'ultima novità di casa Roborock per la pulizia di casa: un robot completo, forte di un sistema di lavaggio dei pavimenti basato su rullo che si estende a seguire il profilo delle pareti abbinato ad un potente motore di aspirazione con doppia spazzola laterale
Alpine A290 alla prova: un'auto bella che ti fa innamorare, con qualche limite
Alpine A290 alla prova: un'auto bella che ti fa innamorare, con qualche limite
Abbiamo guidato per diversi giorni la Alpine A290, la prima elettrica del nuovo corso della marca. Non è solo una Renault 5 sotto steroidi, ha una sua identità e vuole farsi guidare
Recensione HONOR Magic 8 Lite: lo smartphone indistruttibile e instancabile
Recensione HONOR Magic 8 Lite: lo smartphone indistruttibile e instancabile
Abbiamo provato a fondo il nuovo Magic 8 Lite di HONOR, e per farlo siamo volati fino a Marrakech , dove abbiamo testato la resistenza di questo smartphone in ogni condizione possibile ed immaginabile. Il risultato? Uno smartphone praticamente indistruttibile e con un'autonomia davvero ottima. Ma c'è molto altro da sapere su Magic 8 Lite, ve lo raccontiamo in questa recensione completa.
Tutti gli articoli Tutte le news

Vai al Forum
Rispondi
 
Strumenti
Old 23-06-2014, 22:57   #1
SubSeven
Member
 
L'Avatar di SubSeven
 
Iscritto dal: Nov 2009
Messaggi: 105
[JAVA] Multithreading & Swing

"Meglio un morto in casa, che una professoressa inaffidabile sull'uscio"

Tra le sensazioni più belle che la vita universitaria mi sta facendo sperimentare, sicuramente rientra il fatto di dover sostenere prove d'esame che contengono argomenti mai trattati a lezione. Non sarò il primo, ma nemmeno l'ultimo

Veniamo a noi.
Per l'esame di Programmazione Orientata agli Oggetti, avrò da sviluppare un programmino in Java (Netbeans) all'interno del quale dovrò gestire la mutua esclusione tra più thread che condividono una struttura dati in comune.

Secondo il testo: "L'applicativo da implementare consiste di un main che istanzia 4 thread di gestione (T1, T2, T3, T4) [...] Ciascun Thread dispone di una propria GUI che:
• espone un bottone: “inserisci” che se premuto causa l'inserimento di tale utente all'interno del Database (tale operazione di produzione deve essere implementata in maniera esclusiva rispetto agli altri Thread e in coerenza con
l'algoritmo dei produttori/consumatori)
• espone un bottone “cancella” che se premuto causa l'estrazione (e quindi cancellazione) del medesimo utente dal Database (tale operazione di consumo deve essere implementata in maniera esclusiva rispetto agli altri Thread e
in coerenza con l'algoritmo dei produttori/consumatori)"


Creata la GUI (un JFrame), dal main istanzio 4 Thread e li eseguo:

Codice:
Thread T1 = new Thread (new GUI(db));
        T1.start();
        Thread T2 = new Thread (new GUI(db));
        T2.start();
        Thread T3 = new Thread (new GUI(db));
        T3.start();
        Thread T4 = new Thread (new GUI(db));
        T4.start();
Quello che ottengo è la visualizzazione di 4 finestre diverse, tutte con la stessa GUI.
Ogni finestra ha dei campi di testo per l'inserimento di dati, un pulsante INSERISCI (per aggiungere un elemento alla struttura dati) e un pulsante RIMUOVI (per rimuoverlo, appunto).
Sia l'inserimento, sia la rimozione mettono in Sleep (per 2 secondi) il thread da cui è stata lanciata l'azione.
Il corretto svolgimento dell'esercizio prevede che, una volta iniziata l'operazione con il thread 1, cliccando sul pulsante del thread 2, l'azione di quest'ultimo non parta prima del completamento di quella iniziata nel thread 1.

Qual è il problema?
Dal momento che faccio eseguire il codice per l'inserimento/cancellazione di un elemento dalla struttura dati direttamente dal bottone della GUI, visto che quest'ultima azione viene affidata all'EDT (Event Dispatching Thread), mi si bloccano le UI di tutte e 4 le finestre aperte, non consentendomi di fare altro prima della fine del processo avviato.

La mia domanda è: come fare a far eseguire il codice su 4 thread veramente separati?
__________________
CPU Intel Core i3-3217U @1.80GHz |RAM 4GB DD3 | HD Samsung Solid State Drive 840 | S.O. Microsoft Windows 7

Ultima modifica di SubSeven : 23-06-2014 alle 23:01.
SubSeven è offline   Rispondi citando il messaggio o parte di esso
Old 24-06-2014, 06:43   #2
sottovento
Senior Member
 
L'Avatar di sottovento
 
Iscritto dal: Nov 2005
Città: Texas
Messaggi: 1722
Ti capisco. Sono passati (troppi) anni dalla laurea, ma ho ancora il rancore verso certi professori.

Veniamo a noi. Ovviamente occorrerebbe vedere il software che hai scritto, ma da quanto hai raccontato, hai ALMENO 6 thread che girano nel tuo sistema:
- il thread main
- il thread di Swing per la gestione dell'HMI
- T1, T2, T3 e T4

Non importa quante finestre decidi di aprire, saranno tutte gestite dal thread di cui sopra.
E' quindi evidente che se, nella callback (vale a dire nella actionPerformed() in risposta alla pressione di un bottone) metti un ritardo di 2 secondi, allora TUTTE le finestre saranno bloccate per due secondi, perche' il thread swing dovra' eseguire quella pausa e non avra' modo di rispondere agli altri eventi dell'HMI (pressione di altri bottoni, disegno delle finestre, ecc.).
La pausa deve essere fatta dai TUOI thread t1...t4, non dal thread (unico) dell'HMI.

Puoi postare qui il codice, se ti va. Per inciso: non e' che la prof. copia gli esempi direttamente dal libro alla lavagna? Mi e' capitato anche questo
__________________
In God we trust; all others bring data
sottovento è offline   Rispondi citando il messaggio o parte di esso
Old 24-06-2014, 10:54   #3
SubSeven
Member
 
L'Avatar di SubSeven
 
Iscritto dal: Nov 2009
Messaggi: 105
Quote:
Originariamente inviato da sottovento Guarda i messaggi
Per inciso: non e' che la prof. copia gli esempi direttamente dal libro alla lavagna? Mi e' capitato anche questo
Guarda, la mia professoressa è così scarsa che per sbaglio ha proiettato, davanti 100 persone, un documento word con tutte le sue password... il tutto senza nemmeno accorgersene!

Comunque, posto il codice, così almeno rendo un'idea su come ho impostato l'applicativo

Main.java
Codice:
public class Main {
    public static void main (String[] args) {
        ArrayList<Utente> l = new ArrayList<>();
        Database db = new Database (l);                 // Passo la mia struttura dati ad un oggetto di tipo Database, nel quale sono contenuti tutti i metodi synchronized
      
        Thread T1 = new Thread (new GUI(db));
        T1.start();
        Thread T2 = new Thread (new GUI(db));
        T2.start();
        Thread T3 = new Thread (new GUI(db));
        T3.start();
        Thread T4 = new Thread (new GUI(db));
        T4.start();   
    }
Database.java - è la classe che contiene i metodi per l'inserimento/cancellazione di un nuovo elemento all'interno della struttura dati (per adesso la variabile booleana writeLock la sto usando solo per la funzione di inserimento; una volta raggiunto il risultato voluto estenderò la sua implementazione anche alla funzione cancellaUtente )

Codice:
public class Database {

    ArrayList<Utente> l;
    boolean writeLock;
    int readCount;

    public Database(ArrayList<Utente> l) {
        this.l = l;
        writeLock = false;
        readCount = 0;

    }

    private void writeLock() {
        while (writeLock) {
            try {

                Thread.currentThread().wait();

            } catch (InterruptedException ex) {
                Logger.getLogger(Database.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
        this.writeLock = true;

    }

    private void writeUnlock() {

        try {
            Thread.sleep(2000);
        } catch (InterruptedException ex) {
            Logger.getLogger(Database.class.getName()).log(Level.SEVERE, null, ex);
        }
        this.writeLock = false;

        Thread.currentThread().notifyAll();
    }

    public synchronized void nuovoUtente(Utente nuovo) throws UtentePresenteEx {
        writeLock();
        if (l.isEmpty() || (verificaUtente(nuovo) == null)) {
            l.add(nuovo);

        } else {
            throw new UtentePresenteEx(nuovo.getCodice_fiscale());
        }

        writeUnlock();

    }

    private synchronized Object verificaUtente(Utente nuovo) {

        Iterator i = l.iterator();
        while (i.hasNext()) {
            Object ricerca = i.next();
            if (nuovo.compareTo(ricerca) == 1) {
                return ricerca;
            }
        }
        return null;
    }

    public synchronized boolean cancellaUtente(Utente ricerca) throws UtenteNonPresenteEx {

        Object risultato = verificaUtente(ricerca);
        if (risultato != null) {
            if (l.remove((Utente) risultato)) {
                return true;
            } else {
                return false;
            }

        }
        return false;
    }

}
GUI.java (JFrame)
Codice:
public class GUI extends javax.swing.JFrame implements Runnable {

    Database db;

    public GUI(Database db) {
        initComponents();
        this.db = db;
        this.setTitle("Prova JAVA - "+ Thread.currentThread().getName());
    }

    private void inserisciBtnActionPerformed(java.awt.event.ActionEvent evt) {                                             
        inserisciBtn.setEnabled(false);
        String nome = nomeTxt1.getText();
        String cognome = cognomeTxt1.getText();
        String codFisc = codFiscTxt.getText();
        int eta = Integer.parseInt(etaTxt.getText());
        String sesso = sessoTxt.getText();
        String mail = mailTxt.getText();
        Utente nuovo = new Utente(codFisc, nome, cognome, eta, sesso, mail);
        
        try {
            db.nuovoUtente(nuovo);
        } catch (UtentePresenteEx ex) {
            Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }                                            

    private void cancellaBtnActionPerformed(java.awt.event.ActionEvent evt) {                                            
        String codice_fiscale = codFiscTxt.getText();
        Utente ricerca = new Utente(codice_fiscale, null, null, 0, null, null);
        try {
            if (db.cancellaUtente(ricerca)) {
                JOptionPane.showMessageDialog(null, "Utente rimosso con successo", "Informazione", JOptionPane.INFORMATION_MESSAGE);
                deletedUsersTxt.append(ricerca.getCodice_fiscale()+"\n");
            } else throw new UtenteNonPresenteEx(ricerca.getCodice_fiscale());            
        } catch (UtenteNonPresenteEx ex) {
            JOptionPane.showMessageDialog(null, ex.getMessage(), "Errore", JOptionPane.ERROR_MESSAGE);
        } 
    }                                           
    
    @Override
    public void run() {
        setVisible(true);

    }
}
Utente.java - nemmeno a postarla, è una banale classe con i campi (nome, cognome, codice_fiscale, eta, sesso, mail) e relativi Getter & Setter.

Allego, per sicurezza, anche un zip con tutto il progetto (https://dl.dropboxusercontent.com/u/...3/Palestra.zip)

Quote:
Originariamente inviato da sottovento Guarda i messaggi
La pausa deve essere fatta dai TUOI thread t1...t4, non dal thread (unico) dell'HMI.
Ecco, dovrei fare proprio questo! Ma come?
__________________
CPU Intel Core i3-3217U @1.80GHz |RAM 4GB DD3 | HD Samsung Solid State Drive 840 | S.O. Microsoft Windows 7

Ultima modifica di SubSeven : 24-06-2014 alle 10:59.
SubSeven è offline   Rispondi citando il messaggio o parte di esso
Old 24-06-2014, 16:39   #4
sottovento
Senior Member
 
L'Avatar di sottovento
 
Iscritto dal: Nov 2005
Città: Texas
Messaggi: 1722
Scusa il ritardo, sono dovuto partire per le mie "ferie forzate" (ogni mese devo lasciare l'Arabia Saudita e passare qualche giorno in Bahrain per motivi di visto).

Scarichero' la traccia che hai lasciato. Ad ogni modo, i tuoi thread non fanno granche', visto che lavorano solo per qualche microsecondo: rendono la GUI visibile e basta, poi muoiono. Quindi i thread T1...T4 praticamente NON esistono!

Ora ho un po' di cose da fare, poi leggo la traccia. A presto
__________________
In God we trust; all others bring data
sottovento è offline   Rispondi citando il messaggio o parte di esso
Old 24-06-2014, 16:41   #5
SubSeven
Member
 
L'Avatar di SubSeven
 
Iscritto dal: Nov 2009
Messaggi: 105
Quote:
Originariamente inviato da sottovento Guarda i messaggi
Scusa il ritardo, sono dovuto partire per le mie "ferie forzate" (ogni mese devo lasciare l'Arabia Saudita e passare qualche giorno in Bahrain per motivi di visto).

Scarichero' la traccia che hai lasciato. Ad ogni modo, i tuoi thread non fanno granche', visto che lavorano solo per qualche microsecondo: rendono la GUI visibile e basta, poi muoiono. Quindi i thread T1...T4 praticamente NON esistono!

Ora ho un po' di cose da fare, poi leggo la traccia. A presto
Ok, attendo allora
__________________
CPU Intel Core i3-3217U @1.80GHz |RAM 4GB DD3 | HD Samsung Solid State Drive 840 | S.O. Microsoft Windows 7
SubSeven è offline   Rispondi citando il messaggio o parte di esso
Old 24-06-2014, 21:07   #6
VICIUS
Senior Member
 
L'Avatar di VICIUS
 
Iscritto dal: Oct 2001
Messaggi: 11471
Io toglierei qui quattro thread che come ha già detto sottovento hanno vita breve. Piuttosto crea un ExecutorService e passalo alla gui così può usarlo per eseguire dei Runnable contenenti la chiamata al database e la sleep in un thread separato senza bloccare l'interfaccia.

Ti lascio un paio di link alla documentazione per partire
http://docs.oracle.com/javase/7/docs...orService.html
http://docs.oracle.com/javase/7/docs...Executors.html
http://docs.oracle.com/javase/7/docs.../Callable.html
VICIUS è offline   Rispondi citando il messaggio o parte di esso
Old 25-06-2014, 01:56   #7
sottovento
Senior Member
 
L'Avatar di sottovento
 
Iscritto dal: Nov 2005
Città: Texas
Messaggi: 1722
VICIUS, hai perfettamente ragione ma questo e' un esercizio e da quanto ho capito i 4 thread sono richiesti. Occorre provare a dar loro un senso

Ho dato un'occhiata veloce al codice. Per prima cosa, devo dire che mi piace il tuo stile, sei molto pulito. Quindi le modifiche non saranno difficili da eseguire.

La prima modifica da fare riguarda entrambi i metodi inserisciBtnActionPerformed() e cancellaBtnActionPerformed().
Questa e' la tua implementazione:
Codice:
    private void inserisciBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_inserisciBtnActionPerformed
        inserisciBtn.setEnabled(false);
        String nome = nomeTxt1.getText();
        String cognome = cognomeTxt1.getText();
        String codFisc = codFiscTxt.getText();
        int eta = Integer.parseInt(etaTxt.getText());
        String sesso = sessoTxt.getText();
        String mail = mailTxt.getText();
        Utente nuovo = new Utente(codFisc, nome, cognome, eta, sesso, mail);
        
        try {
            db.nuovoUtente(nuovo);
        } catch (UtentePresenteEx ex) {
            Logger.getLogger(GUI.class.getName()).log(Level.SEVERE, null, ex);
        }
    }//GEN-LAST:event_inserisciBtnActionPerformed

    private void cancellaBtnActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancellaBtnActionPerformed
        String codice_fiscale = codFiscTxt.getText();
        Utente ricerca = new Utente(codice_fiscale, null, null, 0, null, null);
        try {
            if (db.cancellaUtente(ricerca)) {
                JOptionPane.showMessageDialog(null, "Utente rimosso con successo", "Informazione", JOptionPane.INFORMATION_MESSAGE);
                deletedUsersTxt.append(ricerca.getCodice_fiscale()+"\n");
            } else throw new UtenteNonPresenteEx(ricerca.getCodice_fiscale());            
        } catch (UtenteNonPresenteEx ex) {
            JOptionPane.showMessageDialog(null, ex.getMessage(), "Errore", JOptionPane.ERROR_MESSAGE);
        } 
    }//GEN-LAST:event_cancellaBtnActionPerformed
La prima istruzione
Codice:
        inserisciBtn.setEnabled(false);
disabilita il bottone, ma non c'e' un codice complementare che lo riattiva. Se il problema e' quello di farlo riattivare dopo 2 secondi, puoi risolverlo modificando direttamente all'interno dei due metodi che hai scritto (ATTENZIONE - quanto scrivo e' java 8):
Codice:
        javax.swing.Timer timer = new javax.swing.Timer(2000, event -> inserisciBtn.setEnabled(true));
        timer.setRepeats(false);
        timer.start();
Alla scadenza del timer, il bottone verra' riattivato.

Il secondo problema e' che l'inserimento/cancellazione viene fatto direttamente da questi due metodi. Devi togliere l'inserimento/cancellazione da li', metterli nei thread T1...T4 ed inventarti un modo per far comunicare i due thread. Secondo me il modo piu' semplice (di cui spesso abuso) e' utilizzare le ArrayBlockingQueue<>. In pratica, Tx e la corrispondente coda si parlano attraverso questa coda: la gui inserisce l'oggetto e il thread tx lo legge ed esegue l'operazione. Semplice, no?
Facendo cosi' dai un senso ai thread e non blocchi l'HMI.

Infine: probabilmente ti e' scappato il fatto che il tuo codice genera un
java.lang.IllegalMonitorStateException

poiche' l'istruzione

Codice:
Thread.currentThread().notifyAll();
e' fuori da qualsiasi blocco sincronizzato, pertanto l'istruzione non e' valida.
Fai attenzione: questo e' il motivo che ti faceva sembrare che l'HMI fosse incartata (insieme ovviamente alla disabilitazione del bottone). Se avessi messo questa istruzione al posto giusto, probabilmente non ti saresti accorto di nulla e non ti saresti chiesto se effettivamente i thread stavano girando correttamente. In pratica, l'errore ti ha dato la possibilita' di "vedere" esattamente le cose come stanno.

Riassumento, i primi passi secondo me sono:
1 - riabilitare il bottone
2 - fare in modo the i thread T1...T4 abbiano un senso, cioe' che dentro il metodo run() ci sia del codice, ed il codice deve essere un ciclo (infinito) che legge dalla coda bloccante che arriva da HMI. Una volta letto il dato (ed un codice operativo che specifica cosa farne), inserisce o cancella il dato;
3 - ripensare lock dei thread, vale a dire la parte che ti fallisce. Pero' io affronterei prima i punti 1 e 2. Per fare questa parte, lascerei stare i monitor ed utilizzerei l'oggetto Semaphore, sempre che la prima soluzione non ti sia stata imposta...

tienici aggiornati
__________________
In God we trust; all others bring data
sottovento è offline   Rispondi citando il messaggio o parte di esso
 Rispondi


Roborock Qrevo Curv 2 Flow: ora lava con un rullo Roborock Qrevo Curv 2 Flow: ora lava con un rull...
Alpine A290 alla prova: un'auto bella che ti fa innamorare, con qualche limite Alpine A290 alla prova: un'auto bella che ti fa ...
Recensione HONOR Magic 8 Lite: lo smartphone indistruttibile e instancabile Recensione HONOR Magic 8 Lite: lo smartphone ind...
Sony WF-1000X M6: le cuffie in-ear di riferimento migliorano ancora Sony WF-1000X M6: le cuffie in-ear di riferiment...
Snowflake porta l'IA dove sono i dati, anche grazie a un accordo con OpenAI Snowflake porta l'IA dove sono i dati, anche gra...
Samsung aggiorna Bixby con One UI 8.5: p...
L'Etiopia vieta le auto a combustione: a...
Pirateria audiovisiva: la Guardia di Fin...
Ubisoft conferma due nuovi Far Cry in sv...
Chi vincerà il Festival di Sanrem...
G42 e Cerebras portano in India un super...
Offerte aggiornate del weekend Amazon: 7...
4 MacBook Air in offerta e scende a 939€...
Chrome cambia il tuo modo di lavorare: o...
Minimo storico iPhone 17 su Amazon: 909€...
USA, incriminati tre ingegneri della Sil...
Xbox: Phil Spencer lascia dopo 38 anni, ...
Cuffie e sostanze chimiche: uno studio e...
Terremoto sui dazi USA: la Corte Suprema...
Arianespace potrebbe lanciare il primo r...
Chromium
GPU-Z
OCCT
LibreOffice Portable
Opera One Portable
Opera One 106
CCleaner Portable
CCleaner Standard
Cpu-Z
Driver NVIDIA GeForce 546.65 WHQL
SmartFTP
Trillian
Google Chrome Portable
Google Chrome 120
VirtualBox
Tutti gli articoli Tutte le news Tutti i download

Strumenti

Regole
Non Puoi aprire nuove discussioni
Non Puoi rispondere ai messaggi
Non Puoi allegare file
Non Puoi modificare i tuoi messaggi

Il codice vB è On
Le Faccine sono On
Il codice [IMG] è On
Il codice HTML è Off
Vai al Forum


Tutti gli orari sono GMT +1. Ora sono le: 16:52.


Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
Served by www3v