View Full Version : [Java] problemi con invokeLater()
giorgio_82
07-07-2009, 17:33
Salve,
è il mio primo post e non so se sono nella sezione giusta. In caso contrario perdonatemi.
Ho il seguente 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 provo ad eseguire mi viene fuori la finestra aperta solo in parte, perchè secondo me sbaglio qualcosa quando vado ad utilizzare la invokeLater() e il programma mi si sospende sull'accept.
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
banryu79
08-07-2009, 08:29
Se provo ad eseguire mi viene fuori la finestra aperta solo in parte, perchè secondo me sbaglio qualcosa quando vado ad utilizzare la invokeLater() e il programma mi si sospende sull'accept.
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.
Hai definito una classe Server che estende un JFrame (e quindi farà parte dell'interfaccia grafica della tua applicazione) ma dentro implementa anche le funzionalità di un server.
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 (http://www.hwupgrade.it/forum/showthread.php?t=2005654) 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 :)
giorgio_82
08-07-2009, 13:27
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? :confused:
Ancora grazie,
Giorgio
banryu79
08-07-2009, 14:06
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?
Precisamente.
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.
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? :confused:
Ancora grazie,
Giorgio
Il fatto è che se lanci la tua interfaccia grafica senza incapsulare il tutto in un Runnable che piazzi con invokeLater nella AWT-Event Queue, la tua interfaccia grafica è potenzialmente soggetta ad errori di rendering (artefatti grafici) e/o altri comportamenti anomali.
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.
Caldwell83
09-07-2009, 11:35
Precisamente.
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.
Mi intrufolo nella discussione.
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?
banryu79
09-07-2009, 11:47
Si passano agli altri tread i riferimenti agli oggetti della swing in modo che li possano modificare quando succede qualcosa?
Credo di sì, (almeno è quello che farei io) con l'accortezza che il thread "esterno" (esterno nel senso che non è l'EDT) quando ha finito e deve fare in modo che la grafica si aggiorni, incapsuli a sua volta le operazioni sui componenti grafici in un nuovo Runnable da accodare con il solito invokeLater.
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.
Si passano agli altri tread i riferimenti agli oggetti della swing in modo che li possano modificare quando succede qualcosa?
I riferimenti agli oggetti Swing devono restare nell'EDT. Ad esempio uno potrebbe pensare di dire:
//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:
final String message = text;
EventQueue.invokeLater(new Runnable() {
public void run() {
jtextArea.append(message);
}
});
Per oggetti mutable basta usare il messaggio come monitor:
//Thread non EDT
Point value = leggi un punto da socket
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);
}
}
});
Ci sono anche altre soluzioni. Ad esempio se lo scambio di messaggi è intenso si può usare una coda concorrente - java.util.concurrent.LinkedBlockingQueue - ed un thread che la consuma passando il contenuto all'edt. La coda garantisce che i valori letti siano gli stessi esistenti al momento dell'immissione.
banryu79
09-07-2009, 15:29
//Thread non EDT
Point value = leggi un punto da socket
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);
}
}
});
Ci sono anche altre soluzioni. Ad esempio se lo scambio di messaggi è intenso si può usare una coda concorrente - java.util.concurrent.LinkedBlockingQueue - ed un thread che la consuma passando il contenuto all'edt. La coda garantisce che i valori letti siano gli stessi esistenti al momento dell'immissione.
Un chiarimento (premetto che di sincronizzazione ci capisco poco o niente): ma cosa garantisce che il valore di message.x passato al metodo append della text area dall'EDT sia ancora lo stesso, rispetto a quando impostato dal thread esterno?
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 :Prrr:
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:
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;
}
}
});
}
Qui ho la garanzia che l'aggiornamento dei valori di p è visibile all'EDT ma non ho una garanzia di ordine. Il codice garantisce che se il Thread A dice:
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.
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
}
}
}
}
Rispetto a prima qui c'è un synchronized in più perchè "p" è supposto essere un campo mentre prima non c'erano campi. Cosa succede se un Thread invoca tre volte il metodo set adesso?
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.
banryu79
10-07-2009, 16:51
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.
banryu79
13-07-2009, 09:33
Eccomi di nuovo :D
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:
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
}
}
}
}
Rispetto a prima qui c'è un synchronized in più perchè "p" è supposto essere un campo mentre prima non c'erano campi.
Preche dici che nell'esempio che hai fatto, precedente a quello quotato, non c'erano campi? il punto "p" non era comunque un campo, anche se dichiarato final? Mi sono perso qualcosa?
La domanda che rimane aperta:
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.
Hai descritto esattamente la situazione che ipotizzavo: anche se sincronizzo l'operazione di scrittura nei campi del punto che poi il mio thread A passa all'EDT, solo in un secondo momento l'EDT preleverà l'operazione da eseguire con quel punto dalla coda: e se nel frattempo un altro thread B accede al punto in scrittura e ne cambia il valore dei campi?
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").
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.
banryu79
13-07-2009, 17:31
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.
Ok grazie, ma questo mi era chiaro già dal tuo esempio.
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.
Anche questo caso l'avevo capito, sempre grazie al tuo esempio.
E' possibile farlo con un solo punto? Sì, se ad ogni set blocchiamo il thread invocante finchè l'edt non abbia terminato la lettura.
Ecco, questo è lo scenario su cui mi sono interrogato, dopo aver letto il tuo primo post a risposta di Caldwell.
Quello che mi (ti) chiedo è: come si fa?
malocchio
14-07-2009, 12:00
OK, mi avete fatto venire mal di testa, per cui adesso scappo a mangiare...:D :Prrr: :rotfl:
No dai scherzo, ho capito quasi tutto. (veramente molto molto interessante)
Per oggetti mutable basta usare il messaggio come monitor:
//Thread non EDT
Point value = leggi un punto da socket
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);
}
}
});
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 !:D
Ciao
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?
E' la variabile message ad essere locale ma se quella variabile è, come nel nostro caso, un riferimento - a oggetto - e quell'oggetto ha dei campi allora i campi a cui accedi tramite quella variabile non sono locali ma risiedono nella memoria condivisa - tra i thread. Da cui la necessità della sincronizzazione.
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?
E' corretto, qui l'istanziazione di un nuovo punto esclusivamente dedicato all'EDT è parte del meccanismo che rende quel codice concorrente. In effetti è lo stesso principio della coda di consumazione solo che la coda che usiamo è quella predefinita per gli eventi AWT/Swing.
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:
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();
}
}
Qui "lock" ha la doppia funzione di escludere l'esecuzione concorrente di set e garantire la visibilità delle mutazione di point all'EDT e di reading ai thread che invocano set. Il Thread che invoca set blocca altri thread in attesa impostando reading a true. L'esecuzione di read da parte dell'EDT cambia il valore di reading da true a false e sblocca i thread in attesa su stop.
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.
malocchio
14-07-2009, 14:14
E' la variabile message ad essere locale ma se quella variabile è, come nel nostro caso, un riferimento - a oggetto - e quell'oggetto ha dei campi allora i campi a cui accedi tramite quella variabile non sono locali ma risiedono nella memoria condivisa - tra i thread. Da cui la necessità della sincronizzazione.
Come sarebbe scusa? Ok, l'istanza (senza la enne, l'avrò sbagliato una decina di volte oggi) risiede nell'heap, e allora? Per accedervi è comunque necessario un reference che punti ad essa, cosa che altri thread non hanno...
E' corretto, qui l'istanziazione di un nuovo punto esclusivamente dedicato all'EDT è parte del meccanismo che rende quel codice concorrente. In effetti è lo stesso principio della coda di consumazione solo che la coda che usiamo è quella predefinita per gli eventi AWT/Swing.
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:
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();
}
}
Qui "lock" ha la doppia funzione di escludere l'esecuzione concorrente di set e garantire la visibilità delle mutazione di point all'EDT e di reading ai thread che invocano set. Il Thread che invoca set blocca altri thread in attesa impostando reading a true. L'esecuzione di read da parte dell'EDT cambia il valore di reading da true a false e sblocca i thread in attesa su stop.
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.
Aiuto... Banryu dicevi che è complicato? Non sono mai arrivato ad implementare sistemi così complessi di sincronizzazione, mi sono fermato ai semafori (Semaphore), a scopo didattico. Devo ammettere che il multithreading è tanto interessante quanto pericoloso, e mi attira fortemente! :stordita:
Il nostro punto message è creato in un metodo ma è poi passato ad un secondo thread, l'edt, tramite invokeLater.
public void set() {
final Point message = new Point(); //message è locale, message.x e message.y no
EventQueue.invokeLater(new Runnable() {
public void run() {
message...
}
});
}
il run() è potenzialmente eseguito da un Thread diverso da quello che esegue set. Da cui la necessità di sincronizzare.
malocchio
14-07-2009, 14:36
Il nostro punto message è creato in un metodo ma è poi passato ad un secondo thread, l'edt, tramite invokeLater.
public void set() {
final Point message = new Point(); //message è locale, message.x e message.y no
EventQueue.invokeLater(new Runnable() {
public void run() {
message...
}
});
}
il run() è potenzialmente eseguito da un Thread diverso da quello che esegue set. Da cui la necessità di sincronizzare.
In questo caso non abbiamo la certezza che il thread che andrà a leggere i campi di message verrà avviato DOPO le nostre chiamate a message.set() ??
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.
malocchio
14-07-2009, 14:51
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.
Ma nello spezzone di codice in questione tu crei il Runnable DOPO le chiamate al set() e POI lo accodi in attesa dell'esecuzione, com'è possibile che il thread relativo a questo runnable parta in concorrenza con le chiamate set() ?
EDIT:
Se io scrivo
System.out.println("1");
new Thread( new Runnable () {
public void run () {
System.out.println("2");
}
}).start();
sono sicuro (in assenza di altri thread), verrà stampato
1
2
O sbaglio?
Tieni conto che l'EDT potrebbe già essere in esecuzione. Diverso sarebbe se l'EDT non lo fosse.
In generale se un Thread A avvia un Thread B - cioè causa l'invocazione del metodo start di quel Thread B - allora ciò che ha fatto il Thread A prima di avviare B è visibile in B - perchè secondo il modello di memoria le azioni compiute da un thread prima dell'avvio di un secondo thread "happens-before" la prima istruzione eseguita dal Thread avviato.
Ma se B è già in esecuzione non c'è la condizione happens-before.
Da notare che i vincoli "happens-before" imponendo che alcuni effetti debbano essere necessariamente visibili tra Thread diversi dicono anche che non è possibile che una certa implementazione di Java causi il riordino delle istruzioni. E' paradossale ma così:
public void metodo() {
EventQueue.invokeLater(new Runnable() {
public void run() {}});
}
un'implementazione di Java è obbligata ad accodare il runnable nella coda degli eventi dopo l'invocazione di metodo - perchè sono azioni inter-thread - ma è libera di eseguire run() prima di metodo() perchè tra run() e metodo() NON C'E un vincolo d'ordine.
banryu79
14-07-2009, 15:14
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.
Intanto ti ringrazio, e molto, per la disponibilità che hai a rispondere, so che richiede tempo e fatica.
Ho letto bene l'esempio dell'algoritmo, e l'ho capito in larga misura anche se mi perdonerai l'ignoranza dei meccanismi degli oggetti Lock e Condition, che non ho mai ne studiato ne usato (appena ho tempo vado a scovarli nella javadoc).
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 accanto 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.
A questo proposito volevo chiederti se fosse corretto gestire la cosa, per esempio, in questo modo:
final Point point = new Point();
final Lock lock = new ReentrantLock();
final Condition stop = lock.newCondition();
boolean reading, closing;
public void set(int x, int y) throws InterruptedException {
lock.lock();
try {
while(reading && !closing) {
stop.await();
}
if (closing) {
// se sto chiudendo l'applicazione posso anche
// infischiarmene del tentativo di mutazione di stato
// di 'point' da parte del thread corrente
stop.signalAll();
}
else {
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();
}
}
public void notifyClosing() {
closing = true;
}
Grazie ancora per le interessanti e chiare spiegazioni, PGI :)
malocchio
14-07-2009, 15:41
Tieni conto che l'EDT potrebbe già essere in esecuzione. Diverso sarebbe se l'EDT non lo fosse.
In generale se un Thread A avvia un Thread B - cioè causa l'invocazione del metodo start di quel Thread B - allora ciò che ha fatto il Thread A prima di avviare B è visibile in B - perchè secondo il modello di memoria le azioni compiute da un thread prima dell'avvio di un secondo thread "happens-before" la prima istruzione eseguita dal Thread avviato.
Ma se B è già in esecuzione non c'è la condizione happens-before.
Da notare che i vincoli "happens-before" imponendo che alcuni effetti debbano essere necessariamente visibili tra Thread diversi dicono anche che non è possibile che una certa implementazione di Java causi il riordino delle istruzioni. E' paradossale ma così:
public void metodo() {
EventQueue.invokeLater(new Runnable() {
public void run() {}});
}
un'implementazione di Java è obbligata ad accodare il runnable nella coda degli eventi dopo l'invocazione di metodo - perchè sono azioni inter-thread - ma è libera di eseguire run() prima di metodo() perchè tra run() e metodo() NON C'E un vincolo d'ordine.
Quando scrivi metodo() intendi la chiamata a metodo() ??
Se così fosse, allora non c'ho capito niente. Com'è possibile che run venga richiamato dall'EDT, prima di avergli passato l'oggetto Runnable su cui verrà richiamato proprio il run() ?
Scusa se ti rompo, è che proprio non riesco a capire questa logica... :rolleyes:
E se invece con metodo intendi l'intero blocco di codice in metodo(), allora siamo d'accordo; non c'è nessuna certezza riguardo all'ordine di esecuzione del codice che viene DOPO la chiamata di InvokeLater, in quanto il thread che sta eseguendo metodo() potrebbe uscire dallo stato di running, ma secondo me il codice che viene PRIMA dell'InvokeLater è certo di essere eseguito prima del codice contenuto nel run(). :mc:
In ogni caso, grazie della tua pazienza :)
C'è un problema con closing perchè l'accesso in scrittura (in notifyClosing) non è sincronizzato con l'accesso in lettura (in set). Ciò significa che un Thread è libero di "non vedere" la mutazione di closing a true che un altro Thread applichi invocando notifyClosing.
Dopodichè dobbiamo metterci nell'ottica "cosa succede se".
E quest'ottica ci dice che il codice che io ho incollato ha un bug.
Cosa succede se l'EDT muore dopo aver eseguito "appendText" e prima di aver eseguito "reading = false"?
Succede che il programma va in stallo perchè reading resta false ed eventuali altri Thread in attesa per eseguire set() non saranno mai risvegliati.
Se morisse dopo non sarebbe un problema perchè il successivo invocante di set lo riattiverebbe. Bisognerebbe spostare il reading = false nel finally E circondare EventQueue.invokeLater con un try-catch(Throwable) { reading = false; }.
A conti fatti la soluzione che creava più punti teneva la porta chiusa ad un sacco di problemi.
Salva quasta chicca che è un problema creato da me il tuo codice è ok. Nota però come notifyClosing sia solo una richiesta di spegnimento e non uno spegnimento immediato. Funziona solo dopo che l'edt ha eseguito stop.notifyAll(): a quel punto i thread risvegliati incontreranno un while in cui reading vale true e (!closing) false quindi salteranno direttamente al finally.
Pensa adesso a quanto sarebbe "divertente" se il meccanismo che volessimo adottare non fosse semplice un "ferma tutto" ma un "avvia-metti in pausa-ferma-riavvia".
Quando scrivi metodo() intendi la chiamata a metodo() ??
Parlo del contenuto. Cioè se avessimo:
public void metodo() {
System.out.println("ciao");
EventQueue.invokeLater(new Runnable() {
public void run() {
System.out.println("mondo");
}
});
}
Potremmo ottenere:
"ciao"
"mondo"
ma anche:
"mondo"
"ciao"
E' la ragione per cui le variabile locali accedute da una classe locale devono essere dichiarate final: obbliga la piattaforma a dare un valore alla variabile prima che quella variabile sia usata nella classe locale. Senza il final l'uso della variabile nella classe locale potrebbe avvenire prima della sua inizializzazione.
malocchio
14-07-2009, 16:10
Parlo del contenuto. Cioè se avessimo:
public void metodo() {
System.out.println("ciao");
EventQueue.invokeLater(new Runnable() {
public void run() {
System.out.println("mondo");
}
});
}
Potremmo ottenere:
"ciao"
"mondo"
ma anche:
"mondo"
"ciao"
E' la ragione per cui le variabile locali accedute da una classe locale devono essere dichiarate final: obbliga la piattaforma a dare un valore alla variabile prima che quella variabile sia usata nella classe locale. Senza il final l'uso della variabile nella classe locale potrebbe avvenire prima della sua inizializzazione.
Mi dispiace, ancora non vedo come possa succedere "mondo\nciao".
Non avresti qualche link interessante sull'argomento?
:D Posso capirlo ma non è una cosa che devi "vedere": devi solo saperla.
Qui c'è un sito dedicato a risorse in merito al modello di memoria:
http://www.cs.umd.edu/~pugh/java/memoryModel/
banryu79
14-07-2009, 16:30
Se morisse dopo non sarebbe un problema perchè il successivo invocante di set lo riattiverebbe. Bisognerebbe spostare il reading = false nel finally E circondare EventQueue.invokeLater con un try-catch(Throwable) { reading = false; }.
Ho provato a integrare:
final Point point = new Point();
final Lock lock = new ReentrantLock();
final Condition stop = lock.newCondition();
boolean reading, closing;
public void set(int x, int y) throws InterruptedException {
lock.lock();
try {
while(reading && !closing) {
stop.await();
}
if (closing) {
stop.signalAll();
}
else {
point.x = x;
point.y = y;
reading = true;
try {
EventQueue.invokeLater(new Runnable() {
public void run() {
read();
}
});
}
catch (Throwable t) {
// EDT e' morto
reading = false;
}
}
} finally {
lock.unlock();
}
}
private void read() {
lock.lock();
try {
int x = point.x;
int y = point.y;
appendText("Letto (" + x + "," + y + ")");
stop.signalAll();
} finally {
reading = false;
lock.unlock();
}
}
public void notifyClosing() {
closing = true;
}
A conti fatti la soluzione che creava più punti teneva la porta chiusa ad un sacco di problemi.
Già, ma mi interessava esplorare un attimo la versione a punto singolo proprio per la sua insidiosità; ero curioso di vedere quanto complesso fosse lo scenario che si andava a delineare...
Nota però come notifyClosing sia solo una richiesta di spegnimento e non uno spegnimento immediato. Funziona solo dopo che l'edt ha eseguito stop.notifyAll(): a quel punto i thread risvegliati incontreranno un while in cui reading vale true e (!closing) false quindi salteranno direttamente al finally.
Non immediatamente, se non sbaglio, prima dovrebbe andare in esecuzione la chiamata a stop.signalAll() [che non so cosa faccia, ma per rispettare la logica del tuo codice di partenza, per simmetria andava incluso]. Sul fatto che sia una sorta di richiesta e non uno spegnimento immediato, sì, questo mi e' chiaro.
Pensa adesso a quanto sarebbe "divertente" se il meccanismo che volessimo adottare non fosse semplice un "ferma tutto" ma un "avvia-metti in pausa-ferma-riavvia".
Troppo in là per me, dovrei prima vedermi con calma le meccaniche sulla sincronizzazione fin qui emerse; immagino sia comunque più complesso.
Quando Chuck Norris programma in Java, non cerca nella documentazione per risolvere i problemi, chiede aiuto a PGI-bis :O
Non immediatamente, se non sbaglio, prima dovrebbe andare in esecuzione la chiamata a stop.signalAll() [che non so cosa faccia, ma per rispettare la logica del tuo codice di partenza, per simmetria andava incluso].
Il Lock è una stanza che ha una porta con relativa serratura e una sola chiave. La porta è chiusa. Chi vuole entrare deve prendere la chiave aprire la porta e chiudersela alle spalle. Quando esce chiude la porta, mette la chiave su un tavolino e se ne va.
Quando c'è qualcuno nella stanza la porta è chiusa e la chiave non è sul tavolino quindi tutti quelli che vogliono entrare devono aspettare che chi è dentro esca e rimetta a disposizione la chiave.
La condizione che derivi da un Lock funziona come un buttafuori.
Ci sono n Thread che vogliono entrare. Uno prende la chiave, apre la porta e se una certa condizione è vera il buttafuori gli frega la chiave, gli mena un cazzotto in faccia che lo fa volare in anticamera lungo disteso, chiude la porta da dentro e butta fuori la chiave passandola sotto la porta.
Finchè il valore di controllo causa l'invocazione di condition.await() chi entra si becca il cazzotto del dolce dormire.
L'invocazione condition.signalAll() è un messaggio al buttafuori di quella porta: piglia e versa una secchiata d'acqua sui poveri disgraziati che hai addormentato a cazzotti.
Quelli si svegliano e si trovano di nuovo davanti alla porta chiusa. Uno prende la chiave, entra, verifica la condizione buttafuori... eccetera eccetera.
Da notare che il fatto di aver preso un pugno in faccia non rende i thread risvegliati prioritari rispetto a qualche altro Thread sopraggiunto nel frattempo davanti alla stessa porta: il classico cornuto e mazziato - con la variante della mazziata preventiva.
malocchio
14-07-2009, 17:03
Il Lock è una stanza che ha una porta con relativa serratura e una sola chiave. La porta è chiusa. Chi vuole entrare deve prendere la chiave aprire la porta e chiudersela alle spalle. Quando esce chiude la porta, mette la chiave su un tavolino e se ne va.
Quando c'è qualcuno nella stanza la porta è chiusa e la chiave non è sul tavolino quindi tutti quelli che vogliono entrare devono aspettare che chi è dentro esca e rimetta a disposizione la chiave.
La condizione che derivi da un Lock funziona come un buttafuori.
Ci sono n Thread che vogliono entrare. Uno prende la chiave, apre la porta e se una certa condizione è vera il buttafuori gli frega la chiave, gli mena un cazzotto in faccia che lo fa volare in anticamera lungo disteso, chiude la porta da dentro e butta fuori la chiave passandola sotto la porta.
Finchè il valore di controllo causa l'invocazione di condition.await() chi entra si becca il cazzotto del dolce dormire.
L'invocazione condition.signalAll() è un messaggio al buttafuori di quella porta: piglia e versa una secchiata d'acqua sui poveri disgraziati che hai addormentato a cazzotti.
Quelli si svegliano e si trovano di nuovo davanti alla porta chiusa. Uno prende la chiave, entra, verifica la condizione buttafuori... eccetera eccetera.
Da notare che il fatto di aver preso un pugno in faccia non rende i thread risvegliati prioritari rispetto a qualche altro Thread sopraggiunto nel frattempo davanti alla stessa porta: il classico cornuto e mazziato - con la variante della mazziata preventiva.
La spiegazione del buttafuori mi ha fatto sganasciare!! :rotfl:
banryu79
14-07-2009, 17:21
Il Lock è una stanza che ha una porta...
[cut]
...il classico cornuto e mazziato - con la variante della mazziata preventiva.
PGI, grazie mille, sei stato cristallino (e divertente, poveri thread :D).
Tra l'altro sto esaminando la documentazione del package java.util.concurrent.lock e appena imbattutomi nella descrizione sommaria di Lock e Condition ho capito che mi mancava un pezzo.
Potevo lasciare la prima implementazione che avevo scritto ma poi editato, quella cioè che semplicemente controlava nell'if se closing era falsa e quindi procedeva con il setting dei campi del punto e l'accodamento della lettura da far eseguire a EDT.
Thank you :)
EDIT:
Certo che un tutorial sulla concorrenza in versione "goliardica" sarebbe una chicca, anche perchè oltre ad una comprensione completa, ci vuole una capacità più unica che rara per spiegare un argomento così tecnico mediante metafore divertenti :D
banryu79
14-07-2009, 17:52
Da notare che il fatto di aver preso un pugno in faccia non rende i thread risvegliati prioritari rispetto a qualche altro Thread sopraggiunto nel frattempo davanti alla stessa porta: il classico cornuto e mazziato - con la variante della mazziata preventiva.
Umm, questo perchè, immagino, a livello logico non c'è assolutamente nessuna relazione diretta tra la condizione derivata dal lock, i thread in (tentato) accesso, e il fatto che qualcuno stia aspettando da più tempo di altri; daltronde così come non c'è una prelazione tra due thread che falliscono l'acquisizione del lock perchè il lock stesso è ancora posseduto da un terzo thread che deve ancora rilasciarlo...
Ma immagino che se nella nostra implementazione avessimo bisogno di stare pure a riconoscere a ogni singolo thread il suo diritto di prelazione, in virtù del tempo di attesa, rispetto gli altri suoi compagni (magari dobbiamo assicurare che l'accesso sia concesso rispettando l'ordine di invocazione del metodo da parte dei thread), il che corrisponderebbe ad accodare in ordine il punto modificato nella coda dell'EDT, dovremmo inventarci qualcosa di terribilmente contorto :D
Tipo un meccanismo che, ad ogni tentativo di acquisizione del lock che non va a buon fine, a causa della condizione derivata sul lock[condition.wait], prende il thread corrente, e lo infila in una coda.
Poi però andrebbe gestito il momento in cui è di nuovo possibile per i thread lockare [condition.signalAll]; bisognerebbe in qualche modo garantire che i tentativi di locking della risorsa da parte dei thread nella coda avvenissero rispettando l'ordine FIFO dei thread nella coda, senza contare dei "nuovi" thread che potrebbero arrivare nel frattempo, ignari di tutto questo bailamme...
Ma forse sto vaneggiando, e non si fa così :D
Non è una necessità peregrina. Se pensi ad un web server e alle richieste che deve gestire in concorso è normale pensare al fatto che debba esistere un qualche politica di correttezza riguardante l'accesso al servizio altrimenti io potrei aspettare cent'anni di avere una pagina web perchè li server mi fa passare davanti tutte le altre richieste che arrivano.
Nel package concurrent si può usare Semaphore che ha un'opzione fairness verificata la quale il metodo acquire() garantisce - poco evangelicamente - che i primi saranno i primi.
banryu79
14-07-2009, 18:29
Nel package concurrent si può usare Semaphore che ha un'opzione fairness verificata la quale il metodo acquire() garantisce - poco evangelicamente - che i primi saranno i primi.
Grazie, vado a studiare.
Javadoc del package e link sul memory model a parte, conosci qualche risorsa paragonabile ai tutorial del Really Big Index (per dare un'idea) che tratti dei meccanismi per la gestione della concorrenza in Java, al di la di quelli implementati direttamente nel linguaggio?
00pipp01
14-07-2009, 20:04
"Posterizza, analizza e rasterizza".. Così diceva il mio prof... Prova...
malocchio
15-07-2009, 09:41
"Posterizza, analizza e rasterizza".. Così diceva il mio prof... Prova...
Sospeso dopo 6 post... i mod sono davvero efficienti!!
@pgi: mi sto leggendo con calma il documento linkato, sto così: :eek: :mbe:
banryu79
16-07-2009, 10:53
@pgi: mi sto leggendo con calma il documento linkato, sto così: :eek: :mbe:
Ho letto anche io l'intro sul nuovo memory model per il linguaggio Java, molto interessante.
Ho trovato utile anche questo link: http://www.ibm.com/developerworks/java/library/j-jtp0618.html
Se non ho capito male (cerco conferma da chi può darla) il "nuovo" memory model è arrivato con la versione 5 di Java, pertanto è impementato dalle JVM di versione 1.5 o superiori, corretto?
Inoltre ho una domanda per PGI, in riferimento allo spezzone di codice precedente, su cui abbiamo ragionato.
C'è un problema con closing perchè l'accesso in scrittura (in notifyClosing) non è sincronizzato con l'accesso in lettura (in set). Ciò significa che un Thread è libero di "non vedere" la mutazione di closing a true che un altro Thread applichi invocando notifyClosing.
A tale proposito, per risolvere non sarebbe sufficiente dichiarare la variabile notifyClosing volatile (riferendoci al nuovo memory model)?
Grazie :)
Sì puoi usare anche volatile - o AtomicBoolean.
Non so se il nuovo memory model sia valido solo a partire da Java 1.5. In teoria è una migliore specificazione di qualcosa che avrebbe dovuto già esistere ma se è stata necessaria significa che qualcosa che non andava nei precedenti JRE c'era.
banryu79
16-07-2009, 17:20
Sì puoi usare anche volatile - o AtomicBoolean.
Non so se il nuovo memory model sia valido solo a partire da Java 1.5. In teoria è una migliore specificazione di qualcosa che avrebbe dovuto già esistere ma se è stata necessaria significa che qualcosa che non andava nei precedenti JRE c'era.
Grazie della risposta :)
Mah, siccome in parte rigurda anche ciò che il compilatore e la virtual machine possono e non possono fare [per esempio la faccenda del riordino di letture e scritture tra variabili volatile e variabili "normali"] e anche le specifiche per la JVM suppongo che uno la garanzia di lavorare con il nuovo memory model ce l'ha solo da una certa versione della piattaforma in poi.
Prima ti chiedevo se conosci una risorsa a mo' di tutorial sull'uso delle classi del package java.util.concurrent, da affiancare alla javadoc per motivi di studio.
di online non ho nulla ma è tutto quanto spiegato in Concurrent Programming in Java di Doug Leaa che era poi a capo del JSR 166
banryu79
16-07-2009, 17:35
di online non ho nulla ma è tutto quanto spiegato in Concurrent Programming in Java di Doug Leaa che era poi a capo del JSR 166
Ah, Doug Lea!
Ottimo grazie, comunque qualcosa ho trovato (anche se datato), proprio grazie al nome. Posto il link (http://g.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/intro.html) per condividere con chiunque sia interessato.
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.