|
|
|
![]() |
|
Strumenti |
![]() |
#1 |
Junior Member
Iscritto dal: Jul 2009
Messaggi: 4
|
[Java] problemi con invokeLater()
Salve,
è il mio primo post e non so se sono nella sezione giusta. In caso contrario perdonatemi. Ho il seguente codice: Codice:
package main; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.net.ServerSocket; import java.net.Socket; import javax.swing.JFrame; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.SwingUtilities; public class Server extends JFrame { public static int port = 2222; private JScrollPane jScrollPane1; private JTextArea textArea; public Server() { initComponents(); setVisible(true); try { ServerSocket ss = new ServerSocket(port); while (true) { textArea.append("in attesa...\n"); Socket socket = ss.accept(); DataInputStream dis = new DataInputStream(new BufferedInputStream(socket.getInputStream())); DataOutputStream dos = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream())); String s = dis.readUTF(); textArea.append(s+"\n"); textArea.append("richiesta effettuata\n"); socket.close(); } } catch (Throwable e) { e.printStackTrace(); } } private void initComponents() { jScrollPane1 = new JScrollPane(); textArea = new JTextArea(); setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE); textArea.setColumns(25); textArea.setEditable(false); textArea.setRows(15); jScrollPane1.setViewportView(textArea); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane()); getContentPane().setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addContainerGap(125, Short.MAX_VALUE) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addGap(109, 109, 109)) ); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addGap(97, 97, 97) .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addContainerGap(107, Short.MAX_VALUE)) ); pack(); } public static void main(String args[]) { SwingUtilities.invokeLater(new Runnable() { public void run() { new Server(); } }); } } Se tolgo, nel main, la chiamata alla invokeLater, cioè faccio solo new Server(), il programma mi va, però ho il dubbio che non sia il modo più giusto di fare il tutto. Mi potreste dire cosa sbaglio e come sarebbe più corretto realizzare l'applicazione!? Vi ringrazio in anticipo. Giorgio Ultima modifica di giorgio_82 : 07-07-2009 alle 20:30. |
![]() |
![]() |
![]() |
#2 | |
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
Quote:
Come forse già sai, dato che usi l'invokeLater per accodare nella coda degli eventi di Swing la realizzazione di un nuovo oggetto Server, le operazioni programmatiche cha hanno come oggetto componenti di Swing visualizzabili vanno fatte eseguire all'Event Dispatcher Thread (EDT). Fin qui tutto bene. Il problema è che al momento della chiamata al costruttore [new Server()] eseguito dall'EDT, l'EDT stesso esegue: la initComponents() [usi NetBeans, vero?] e va bene, ma poi deve eseguire anche il ciclo dove c'è la accept() e le altre operazioni che fai con il ServerSocket. A questo punto l'EDT rimane vincolato ad eseguire questo tuo codice (non viene mai "liberato" finchè rimane in vita il ciclo) e quindi non può continuare con il suo lavoro: cioè prelevare eventi dalla coda degli eventi di Swing ed eseguirli... In altre parole la grafica "si inchioda" (per sempre, tra l'altro, dato che non verrà mai terminato il ciclo while...) Qui trovi un ottimo tutorial sulle basi di Swing dove è spiegato tutto per filo e per segno (e in modo molto più chiaro di come potrei fare io) già nelle prime pagine. Fortemente consigliato ![]()
__________________
As long as you are basically literate in programming, you should be able to express any logical relationship you understand. If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it. (Chris Crawford) |
|
![]() |
![]() |
![]() |
#3 |
Junior Member
Iscritto dal: Jul 2009
Messaggi: 4
|
Grazie mille per la risposta. Leggero il tutorial con attenzione. Comunque se ho ben capito devo far fare i compiti del server ad un altro thread e non all'EDT? Devo cioè mettere il while(true) {.....} nel run di un altro thread?
Un'altra cosa: come ho già detto, se tolgo l'invokeLater() e nel main chiamo direttamente new Server(); l'applicazione mi va. Quindi, evidentemente il thread che si occupa della grafica e il thread che si occupa di fare da server sono diversi? Ma allora l'invokeLate() non servirebbe? ![]() Ancora grazie, Giorgio |
![]() |
![]() |
![]() |
#4 | ||
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
Quote:
Se devi ancora lavorare con Swing quel tutorial è veramente utile ![]() Inpratica dovresti incapsulare quella parte in un Runnable da lanciare subito. Poi magari può capitare che a seguito di operazioni che avvengono in thread "esterni" uno deve aggiornare o comunque compiere azioni grafiche; da cui la neccessità di sapere come sincronizzare il tutto. Non a caso nel JDK esistono delle classi di utility, tipo la SwingWorker, proprio per agevolare operazioni del genere. Alla fine del tutorial linkato c'è una breve introduzione proprio su SwingWorker. Quote:
In pratica può succedere che per interfacce semplici (come quella in oggetto) il problema può non verificarsi finchè fai girare l'applicativo per poco tempo e sempre sotto lo stesso sistema: magari i problemi cominciano a manifestarsi più tardi, quando l'applicazione la complessità dell'interfaccia cresce o quando si prova a far girare l'applicativo sotto diversi sistemi... La morale della favola è che è sempre errato, dal punto di vista architetturale, il non rispetto della condizione percui tutte le operazioni relative a componenti Swing devono essere eseguite dall'EDT. E questo proprio perchè l'architettura di Swing è a thread singolo.
__________________
As long as you are basically literate in programming, you should be able to express any logical relationship you understand. If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it. (Chris Crawford) Ultima modifica di banryu79 : 08-07-2009 alle 14:10. |
||
![]() |
![]() |
![]() |
#5 | |
Senior Member
Iscritto dal: Nov 2007
Città: Bergamo
Messaggi: 307
|
Quote:
Ho notato che nella api di java 1.5 la classe SwingWorker non esiste, quindi come si fa? Si passano agli altri tread i riferimenti agli oggetti della swing in modo che li possano modificare quando succede qualcosa? |
|
![]() |
![]() |
![]() |
#6 | |
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
Quote:
Ovviamente tutta questa trafila viene fatta solo per quelle operazioni così pesanti che, se lasciate svolgere dall'EDT, lo impegnano per troppo tempo tanto da rallentare sensibilmente la reattività della GUI della applicazione. Altrimenti va bene evitare di sbattersi e lasciar sgobbare l'EDT. Penso che l'utente PGI-bis possa darti informazioni più precise in merito, data la sua esperienza.
__________________
As long as you are basically literate in programming, you should be able to express any logical relationship you understand. If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it. (Chris Crawford) |
|
![]() |
![]() |
![]() |
#7 | |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Quote:
//nel thread EDT final JFrame frame = new JFrame() ... bla bla frame.setVisible(true); new Thread() { public void run() { fai qualcosa con frame....} };//errato Non va bene perchè l'edt continua a manipolare il frame - per visualizzarlo, spostarlo, aggiornarlo eccetera - mentre il secondo thread cerca di fare qualcosa con lo stesso frame. Vale per tutti i componenti Swing. Non è possibile usare la sincronizzazione perchè non abbiamo modo di dire all'EDT "tutte le operazioni che fai sul componente devi farle usando il monitor X": possiamo dirlo solo ai thread di cui abbiamo il controllo. Bisogna quindi separare la parte del programma che gestisce l'interfaccia da eventuali altri Thread e far comunicare le due parti attraverso oggetti-messaggio immutabili o sincronizzati. Ad esempio se sono in un Thread che legge del testo da un socket: //Thread NON edt String text = leggi testo da socket Per passare il testo letto ad una JTextArea dirò una cosa tipo: Codice:
final String message = text; EventQueue.invokeLater(new Runnable() { public void run() { jtextArea.append(message); } }); //Thread non EDT Point value = leggi un punto da socket Codice:
final Point message = new Point(); synchronized(message) { message.x = value.x; message.y = value.y; } EventQueue.invokeLater(new Runnable() { public void run() { synchronized(message) { jtextArea.append(message.x + "," + message.y); } } });
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
|
![]() |
![]() |
![]() |
#8 | |
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
Quote:
Se il lasso di tempo cha passa tra setting del valore da parte del thread esterno che passa il malloppo all'EDT e il momento in cui l'EDT effettivamente recupera l'istruzione/evento da processare è abbastanza ampio, in caso di potenziale accesso concorrente all'oggetto referenziato da messagge non c'è il rischio che un altro thread ci modifichi sotto i piedi il valore? E' giusto come ragionamento (cioè esiste la possibilità che si verifichi tale scenario)? E' in questi casi che potremmo cercare garanzie al riguardo tamite quella LinkedBlockingQueue che hai nominato? Ehm, a proposito, in sostanza che fa? Blocca l'accesso all'oggetto finchè non viene prelevato dalla coda? Oppure tiene solo una copia dei valori tali quali erano all'epoca dell'inserimento? Spero di non aver scassato troppo con tutte queste domande, eh ![]()
__________________
As long as you are basically literate in programming, you should be able to express any logical relationship you understand. If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it. (Chris Crawford) |
|
![]() |
![]() |
![]() |
#9 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Se ho due Thread A e B entrambi in esecuzione, A crea un Point - oggetto tipicamente mutabile - di coordinate (100, 75) e lo passa a B tout court il punto che B riceve può essere un qualsiasi valore tra un punto di coordinate (0,0), un punto di coordinate (100,0) e un punto (0,75). Questi sono i valori che B può legittimamente attendersi.
La sincronizzazione ci dice che se ho un monitor M e il Thread A assegna al punto le sue coordinate sincronizzandosi su M una successiva lettura del punto da parte di un Thread B sincronizzato su M legge necessariamente i valori precedentemente assegnati da A. Nota bene: precedentemente. La sincronizzazione non è perpetua. Abbiamo un punto P. C'è un metodo che assegna delle coordinate a P sincronizzandosi su P stesso e poi delega la lettura di P all'EDT. Una cosa tipo: Codice:
private final Point p = new Point();//final = p non è mai null public void set(int x, int y) { synchronized(p) { p.x = x; p.y = y; } EventQueue.invokeLater(new Runnable() { public void run() { synchronized(p) { leggi i valori p.x e p.y; } } }); } set(1, 1); set(2, 2); set(3, 3); l'edt può vedere una qualsiasi tripla composta dai punti ( (1,1), (2,2), (3,3) ) e non vedrà mai (0,1) o (0,0) o (1,0) o (0,2) o (3,1). Vale a dire che non esiste una race condition riguardante i valori del punto ma esiste una race condition tra le invocazioni di set da parte del Thread A e le letture dell'EDT. Per evitare anche la seconda race condition occorre generare un nuovo punto ad ogni invocazione di set e passare quel punto all'EDT come messaggio: così facendo ogni invocazione di set genera un nuovo punto e l'eventuale invocazione plurima di set da parte del Thread A non lede la lettura dei valori successivamente impostati da parte dell'EDT. Codice:
private final Point p = new Point(); public synchronized void set(int x, int y) { p.x = x; p.y = y; final Point message = new Point(); synchronized(message) { message.x = x; message.y = y; } EventQueue.invokeLater(new Runnable() { public void run() { synchronized(message) { leggi message.x e message.y } } } } Succede che quel Thread crea tre punti diversi - uno ad ogni invocazione - ed ognuno di quei punti è passato all'edt per una lettura sincronizzata. Ergo ora se il Thread A dice: set(1, 1); set(2, 2); set(3, 3); l'Edt legge necessariamente: (1,1) (2,2) (3,3) Capita perchè rispetto ad A le tre invocazioni successive di set sono necessariamente ordinate e in più ognuna di queste crea un nuovo punto - e non agisce su un unico punto come prima. Ma anche qui c'è race condition se anzichè parlare di un solo Thread A iniziamo ad avere due Thread, A e B, che invocano set. Qui occorrerà stabilire se esista un ordine necessario tra le invocazioni di set da parte di A e B e in caso affermativo si procederà a garantire tale ordine.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#10 |
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
Grazie della risposta: causa programmazione forzata e scarse ore di sonno non ci ho capito quasi una mazza (e va beh, la verità è che di multithreading non ne so nulla, a parte quelle cosine base che leggi sui tutorial), ma mi riservo la possibilità di leggere e riflettere con calma in futuro ed eventualmente tampinarti con altre domande.
__________________
As long as you are basically literate in programming, you should be able to express any logical relationship you understand. If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it. (Chris Crawford) |
![]() |
![]() |
![]() |
#11 | ||
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
Eccomi di nuovo
![]() Ho riletto e ri-riflettuto con calma e matita alla mano sul tuo esempio, e sono riuscito a capirlo. Ammazza' il multithreading è molto più complesso di quanto sospettassi ![]() Comunque mi rimane un dubbio da dipanare, e una domanda da porre. Il dubbio: Quote:
La domanda che rimane aperta: Quote:
E' neccessario usare due punti distinti, uno per thread, e ogni thread poi delega le operazioni all'EDT sul proprio punto? E' possibile farlo usando un solo punto, condiviso da entrabi i thread e dall'EDT? Ciao e grazie dei chiarimenti ![]() P.S.: per chiarezza, l'ordine neccessario tra le invocazioni di set da parte di A e B sarebbe il seguente: Per ogni invocazione di set sul punto da parte di uno dei due thread, non può essere eseguita un'altra invocazione di set da parte dell'altro thread finchè le operazioni su quel punto delegate all'EDT non siano state espletate. Ovvero se A set"P" e inserisce il messaggio "P" nella coda degli eventi di EDT, B non può fare set"P" finchè l'operazione delegata non sia stata effettivamente eseguita da EDT (B rimane in attesa su "P").
__________________
As long as you are basically literate in programming, you should be able to express any logical relationship you understand. If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it. (Chris Crawford) Ultima modifica di banryu79 : 13-07-2009 alle 09:40. |
||
![]() |
![]() |
![]() |
#12 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Il prima era riferito al prima di prima. Là non c'erano campi.
La seconda questione era diversa. Il problema che poni non c'è per via della sincronizzazione sul metodo. synchronized void set(...) esclude che due Thread possano eseguire contemporaneamente questo metodo. C'è sempre un solo thread per volta che esegue set. Non è quindi possibile che mentre il Thread A modifica il punto in set un Thread B faccia lo stesso, almeno non tramite il metodo set. Poichè il metodo set genera un nuovo punto da dare in pasto all'EDT non sussiste neppure il problema di un'eventuale sovrapposizione tra l'esecuzione di set da parte di un Thread A o B e la lettura del valore impostato da parte dell'EDT. I punti non sono due, sono uno più un nuovo punto ad ogni invocazione di set. E' possibile farlo con un solo punto? Sì, se ad ogni set blocchiamo il thread invocante finchè l'edt non abbia terminato la lettura.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#13 | |||
Senior Member
Iscritto dal: Oct 2007
Città: Padova
Messaggi: 4131
|
Quote:
Quote:
Quote:
Quello che mi (ti) chiedo è: come si fa?
__________________
As long as you are basically literate in programming, you should be able to express any logical relationship you understand. If you don’t understand a logical relationship, you can use the attempt to program it as a means to learn about it. (Chris Crawford) |
|||
![]() |
![]() |
![]() |
#14 | |
Senior Member
Iscritto dal: Feb 2007
Città: Verona
Messaggi: 1060
|
OK, mi avete fatto venire mal di testa, per cui adesso scappo a mangiare...
![]() ![]() ![]() No dai scherzo, ho capito quasi tutto. (veramente molto molto interessante) Quote:
Ora, un paio di domande: 1. l'oggetto message è un'instanza appena creata, a cosa serve il blocco sincronizzato su di esso synchronized(message) { message.x = value.x; message.y = value.y; } ?? Se l'oggetto è stato creato all'interno del metodo, c'è un solo thread in grado di accedervi, per cui a cosa serve acquisire il lock su message? 2. Tutto questo (ovvero il fatto che message sicuramente non viene modificato prima di essere praticamente utilizzato dall'EDT) funziona solo se reference di message non "escono" dal blocco di codice del metodo. In caso contrario un codice "esterno" potrebbe modificare message prima che l'EDT l'abbia utilizzato, o sbaglio? Spero di non aver detto ca@@ate, ho fame e, si sa, a stomaco vuoto si ragiona meglio ! ![]() Ciao
__________________
|
|
![]() |
![]() |
![]() |
#15 | ||
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Quote:
Quote:
Per quanto riguarda l'uso di un solo punto l'algoritmo è breve. Supponendo che read sia l'operazione dell'EDT sul punto e set sia l'invocazione del nostro metodo che imposta i valori del punto successivamente letto dall'EDT ciò che vogliamo è: finchè l'EDT non ha terminato l'operazione read qualsiasi thread che invochi set è bloccato; quando l'EDT termina l'operazione read sveglia uno qualsiasi dei thread sospesi in set. La coppia set-read con relativi campi di controllo potrebbe essere: Codice:
final Point point = new Point(); final Lock lock = new ReentrantLock(); final Condition stop = lock.newCondition(); boolean reading; public void set(int x, int y) throws InterruptedException { lock.lock(); try { while(reading) { stop.await(); } point.x = x; point.y = y; reading = true; EventQueue.invokeLater(new Runnable() { public void run() { read(); } }); } finally { lock.unlock(); } } private void read() { lock.lock(); try { int x = point.x; int y = point.y; appendText("Letto (" + x + "," + y + ")"); reading = false; stop.signalAll(); } finally { lock.unlock(); } } Ovviamente qui siamo nel caso scolastico. In realtà la faccenda richiede un po' più di codice perchè in un'applicazione reale noi dovremmo pensare anche alla possibilità che l'applicazione debba terminare mentre il nostro set-read è in funzione. Significa cioè che accando all'avvenuta lettura di point da parte dell'EDT esiste una seconda condizione che sblocca tutti i Thread in attesa, tale condizione è la richiesta di spegnimento del programma e a differenza di reading essa non deve condurre all'invocazione di read da parte dell'edt.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
||
![]() |
![]() |
![]() |
#16 | ||
Senior Member
Iscritto dal: Feb 2007
Città: Verona
Messaggi: 1060
|
Quote:
Quote:
![]()
__________________
|
||
![]() |
![]() |
![]() |
#17 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Il nostro punto message è creato in un metodo ma è poi passato ad un secondo thread, l'edt, tramite invokeLater.
Codice:
public void set() { final Point message = new Point(); //message è locale, message.x e message.y no EventQueue.invokeLater(new Runnable() { public void run() { message... } }); }
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#18 | |
Senior Member
Iscritto dal: Feb 2007
Città: Verona
Messaggi: 1060
|
Quote:
__________________
|
|
![]() |
![]() |
![]() |
#19 |
Senior Member
Iscritto dal: Nov 2004
Città: Tra Verona e Mantova
Messaggi: 4553
|
Non ce l'abbiamo. Solo le azioni che vengono eseguite in uno stesso Thread avvengono nell'ordine in cui sono scritte nel codice.
run() e set() sono metodi eseguiti da Thread diversi. Secondo il modello di memoria possono essere eseguiti in qualsiasi ordine - perchè non essendo soggetti ad un vincolo happens-before le invocazioni possono essere riordinate.
__________________
Uilliam Scecspir ti fa un baffo? Gioffri Cioser era uno straccione? E allora blogga anche tu, in inglese come me! |
![]() |
![]() |
![]() |
#20 | |
Senior Member
Iscritto dal: Feb 2007
Città: Verona
Messaggi: 1060
|
Quote:
EDIT: Se io scrivo Codice:
System.out.println("1"); new Thread( new Runnable () { public void run () { System.out.println("2"); } }).start(); Codice:
1 2
__________________
Ultima modifica di malocchio : 14-07-2009 alle 14:56. |
|
![]() |
![]() |
![]() |
Strumenti | |
|
|
Tutti gli orari sono GMT +1. Ora sono le: 17:15.