Torna indietro   Hardware Upgrade Forum > Software > Programmazione

Il cuore digitale di F1 a Biggin Hill: l'infrastruttura Lenovo dietro la produzione media
Il cuore digitale di F1 a Biggin Hill: l'infrastruttura Lenovo dietro la produzione media
Nel Formula 1 Technology and Media Centre di Biggin Hill, la velocità delle monoposto si trasforma in dati, immagini e decisioni in tempo reale grazie all’infrastruttura Lenovo che gestisce centinaia di terabyte ogni weekend di gara e collega 820 milioni di spettatori nel mondo
DJI Osmo Mobile 8: lo stabilizzatore per smartphone con tracking multiplo e asta telescopica
DJI Osmo Mobile 8: lo stabilizzatore per smartphone con tracking multiplo e asta telescopica
Il nuovo gimbal mobile DJI evolve il concetto di tracciamento automatico con tre modalità diverse, un modulo multifunzionale con illuminazione integrata e controlli gestuali avanzati. Nel gimbal è anche presente un'asta telescopica da 215 mm con treppiede integrato, per un prodotto completo per content creator di ogni livello
Recensione Pura 80 Pro: HUAWEI torna a stupire con foto spettacolari e ricarica superveloce
Recensione Pura 80 Pro: HUAWEI torna a stupire con foto spettacolari e ricarica superveloce
Abbiamo provato il nuovo HUAWEI Pura 80 Pro. Parliamo di uno smartphone che è un vero capolavoro di fotografia mobile, grazie ad un comparto completo in tutto e per tutto, In questa colorazione ci è piaciuto molto, ma i limiti hardware e software, seppur in netto miglioramento, ci sono ancora. Ma HUAWEI ha fatto davvero passi da gigante per questa nuova serie Pura 80. Buona anche l'autonomia e soprattutto la ricarica rapida sia cablata che wireless, velocissima.
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


Il cuore digitale di F1 a Biggin Hill: l'infrastruttura Lenovo dietro la produzione media Il cuore digitale di F1 a Biggin Hill: l'infrast...
DJI Osmo Mobile 8: lo stabilizzatore per smartphone con tracking multiplo e asta telescopica DJI Osmo Mobile 8: lo stabilizzatore per smartph...
Recensione Pura 80 Pro: HUAWEI torna a stupire con foto spettacolari e ricarica superveloce Recensione Pura 80 Pro: HUAWEI torna a stupire c...
Opera Neon: il browser AI agentico di nuova generazione Opera Neon: il browser AI agentico di nuova gene...
Wind Tre 'accende' il 5G Standalone in Italia: si apre una nuova era basata sui servizi Wind Tre 'accende' il 5G Standalone in Italia: s...
PlayStation Portal: arriva lo Streaming ...
iPad Air 13'' (M2) in offerta: sconto di...
Schiaffo al copyright: utilizzare materi...
realme GT 8 Pro porta le fotocamere inte...
JBL Clip 5 a soli 39€ su Amazon: lo spea...
Apple pronta a pagare 1 miliardo l'anno ...
Apple non dimentica gli iPhone più...
Nintendo si prepara a dare il benservito...
Arriva la Canon R6 Mark III con un obiet...
Una famiglia ha ridotto un conto ospedal...
Le carte collezionabili dell'INPS conqui...
Football Manager 26 debutta su Steam con...
A 189,99€ con coupon: il NAS UGREEN che ...
Arm cresce ancora: ricavi oltre 1 miliar...
Xiaomi Redmi Note 14 5G ora a soli 179€:...
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: 11:59.


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