Sono relativamente nuovo a Scala e alla programmazione funzionale, e mi piace l'idea che usando oggetti immutabili posso evitare molte insidie nella sicurezza dei thread. Una cosa mi perseguita ancora ed è l'esempio classico usato per insegnare la sicurezza del thread: il contatore condiviso.Modo funzionale per implementare un contatore condiviso thread-safe
Mi chiedevo se sarebbe stato possibile implementare un contatore thread-safe (un contatore di richieste in questo esempio), utilizzando oggetti immutabili e concetti funzionali ed evitare completamente la sincronizzazione.
Quindi, per riferimento qui sono prima le versioni classiche mutevoli del contatore (scusate per la variabile membro pubblico, solo per brevità degli esempi)
Mutevole, non filo versione a sicurezza:
public class Servlet extends HttpServlet {
public int requestCount = 0;
@Override
public void service(ServletRequest req, ServletResponse res) throws ... {
requestCount++; //thread unsafe
super.service(req, res);
}
}
Mutevole, filo classica versione a sicurezza: (o almeno così spero ...)
public class Servlet extends HttpServlet {
public volatile int requestCount = 0;
@Override
public void service(ServletRequest req, ServletResponse res) throws ... {
synchronized (this) {
requestCount++;
}
super.service(req, res);
}
}
Mi chiedevo se esiste un modo per utilizzare oggetti immutabili e variabili volatili per ottenere la sicurezza del thread senza sincronizzazione.
Quindi ecco il mio ingenuo tentativo. L'idea è di avere un oggetto immutabile per il contatore, e basta sostituire il riferimento ad esso, usando una variabile volatile. Sembra pescoso, ma vale la pena sparare.
Holder:
public class Incrementer {
private final int value;
public Incrementer(final int oldValue) {
this.value = oldValue + 1;
}
public Incrementer() {
this.value = 0;
}
public int getValue() {
return value;
}
}
servlet Modificato:
public class Servlet extends HttpServlet {
public volatile Incrementer incrementer = new Incrementer();
@Override
public void service(ServletRequest req, ServletResponse res) throws ... {
incrementer = new Incrementer(incrementer.getValue());
super.service(req, res);
}
}
Ho una forte sensazione che questo non è anche thread-safe, come sto leggendo da incrementatore, e potrebbe ottenere un valore scaduto (ad es. se il riferimento era già stato sostituito da un altro filo). Nel caso in cui non sia effettivamente thread-safe, allora mi chiedo se non c'è affatto un modo "funzionale" per gestire un tale scenario contatore senza blocco/sincronizzazione.
Quindi la mia domanda (s) sono
- È questo thread-safe per caso?
- Se sì, perché?
- In caso contrario, esiste un modo per implementare tale contatore senza eseguire la sincronizzazione?
Anche se il codice di esempio di cui sopra è in Java, risponde a Scala sono ovviamente i benvenuti
Interessante, ero sicuro che AtomicInteger utilizza internamente la sincronizzazione, quindi non l'ho nemmeno considerato come una risposta, ma guardando il codice sorgente sembra che stia usando altri modi (codice nativo) per raggiungere l'obiettivo: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/concurrent/atomic/AtomicInteger.java –