mad_hhatter
28-01-2008, 16:14
ciao ragazzi, ho un problema di sincronizzazione riguardante la seguente situazione.
Ho una sorgente di eventi gestita tramite il pattern Observer per cui tale sorgente comunica con una serie di observer tramite i 2 metodi tagAdded(), tagRemoved().
Ogni observer è un Adapter, nel senso che converte gli eventi in messaggi aventi un dato formato (faccio questo perché è necessario gestire protocolli di comunicazione diversi).
Ogni observer, quindi, è a sua volta un'entità Observable contenente una lista di client (destinatari finali degli eventi emessi dalla sorgente).
Tralasciando la sorgente e sapendo che le invocazione dei metodi tagAdded e tagRemoved sono temporalmente disgiunte, ho il problema di gestire la lista di client finali in uno degli Adapter.
Vorrei evitare di leggere tale lista in mutua esclusione (per ragioni di performance), tuttavia sono in presenza di scrittori multipli (un thread aggiunge client, un altro rileva i client "morti" e li rimuove). Quindi non posso adottare la tecnica "copy on write".
Se qualcuno avesse la pazienza di dare un'occhiata al codice che ho postato, vorrei discutere la strategia da me utilizzata.
Grazie infinite
public class Main implements RfidListener, Runnable {
private ArrayList<Socket> listeners = new ArrayList<Socket>();
private ServerSocket s;
public Main(int port) throws IOException {
s = new ServerSocket(port);
}
public void run() {
while (true) {
try {
Socket client = s.accept();
addRemoteClient(client);
} catch (IOException e) {}
}
}
@Override
public void tagAdded(RfidTag tag) {
System.out.println("added " + tag.getRFID());
String xmlString = "<rfidItem status=\"added\" id=\""
+ tag.getRFID() + "\"></rfidItem>" + '\0';
sendXmlMessage(xmlString);
}
@Override
public void tagRemoved(RfidTag tag) {
System.out.println("removed " + tag.getRFID());
String xmlString = "<rfidItem status=\"removed\" id=\""
+ tag.getRFID() + "\"></rfidItem>" + '\0';
sendXmlMessage(xmlString);
}
private void addRemoteClient(Socket client) {
synchronized (this) {
if (client == null) {
return;
}
if (!listeners.contains(client)) {
listeners.add(client);
}
}
}
private void deleteRemoteClient(Socket client) {
synchronized (this) {
listeners.remove(client);
}
}
/*
* snapshot viene usato solo da sendXmlMessage()
* e serve a risparmiare allocazioni nell'esecuzione
* di listeners.toArray(). Questa strategia funziona
* solo se le invocazioni di tagAdded(), tagRemoved()
* sono temporalmente disgiunte. Inoltre questa
* strategia ha senso solo se la probabilità che il
* numero di elementi in listeners superi la dimensione
* corrente di snapshot è bassa.
*/
private Socket[] snapshot = new Socket[100];
private void sendXmlMessage(String xmlString) {
if (xmlString == null) {
return;
}
byte[] message = xmlString.getBytes();
synchronized (this) {
snapshot = listeners.toArray(snapshot);
}
for (int i = 0; i < snapshot.length; i++) {
if (snapshot[i] == null) {
break;
}
try {
snapshot[i].getOutputStream().write(message);
} catch (IOException e) {
try {
snapshot[i].close();
} catch (IOException e1) {}
deleteRemoteClient(snapshot[i]);
}
}
}
}
Ho una sorgente di eventi gestita tramite il pattern Observer per cui tale sorgente comunica con una serie di observer tramite i 2 metodi tagAdded(), tagRemoved().
Ogni observer è un Adapter, nel senso che converte gli eventi in messaggi aventi un dato formato (faccio questo perché è necessario gestire protocolli di comunicazione diversi).
Ogni observer, quindi, è a sua volta un'entità Observable contenente una lista di client (destinatari finali degli eventi emessi dalla sorgente).
Tralasciando la sorgente e sapendo che le invocazione dei metodi tagAdded e tagRemoved sono temporalmente disgiunte, ho il problema di gestire la lista di client finali in uno degli Adapter.
Vorrei evitare di leggere tale lista in mutua esclusione (per ragioni di performance), tuttavia sono in presenza di scrittori multipli (un thread aggiunge client, un altro rileva i client "morti" e li rimuove). Quindi non posso adottare la tecnica "copy on write".
Se qualcuno avesse la pazienza di dare un'occhiata al codice che ho postato, vorrei discutere la strategia da me utilizzata.
Grazie infinite
public class Main implements RfidListener, Runnable {
private ArrayList<Socket> listeners = new ArrayList<Socket>();
private ServerSocket s;
public Main(int port) throws IOException {
s = new ServerSocket(port);
}
public void run() {
while (true) {
try {
Socket client = s.accept();
addRemoteClient(client);
} catch (IOException e) {}
}
}
@Override
public void tagAdded(RfidTag tag) {
System.out.println("added " + tag.getRFID());
String xmlString = "<rfidItem status=\"added\" id=\""
+ tag.getRFID() + "\"></rfidItem>" + '\0';
sendXmlMessage(xmlString);
}
@Override
public void tagRemoved(RfidTag tag) {
System.out.println("removed " + tag.getRFID());
String xmlString = "<rfidItem status=\"removed\" id=\""
+ tag.getRFID() + "\"></rfidItem>" + '\0';
sendXmlMessage(xmlString);
}
private void addRemoteClient(Socket client) {
synchronized (this) {
if (client == null) {
return;
}
if (!listeners.contains(client)) {
listeners.add(client);
}
}
}
private void deleteRemoteClient(Socket client) {
synchronized (this) {
listeners.remove(client);
}
}
/*
* snapshot viene usato solo da sendXmlMessage()
* e serve a risparmiare allocazioni nell'esecuzione
* di listeners.toArray(). Questa strategia funziona
* solo se le invocazioni di tagAdded(), tagRemoved()
* sono temporalmente disgiunte. Inoltre questa
* strategia ha senso solo se la probabilità che il
* numero di elementi in listeners superi la dimensione
* corrente di snapshot è bassa.
*/
private Socket[] snapshot = new Socket[100];
private void sendXmlMessage(String xmlString) {
if (xmlString == null) {
return;
}
byte[] message = xmlString.getBytes();
synchronized (this) {
snapshot = listeners.toArray(snapshot);
}
for (int i = 0; i < snapshot.length; i++) {
if (snapshot[i] == null) {
break;
}
try {
snapshot[i].getOutputStream().write(message);
} catch (IOException e) {
try {
snapshot[i].close();
} catch (IOException e1) {}
deleteRemoteClient(snapshot[i]);
}
}
}
}