Funkcjonalny sposób wdrożenia licznika bezpiecznego dla wątków
Jestem stosunkowo nowy w Scali i programowaniu funkcjonalnym i podoba mi się pomysł, że używając niezmiennych obiektów mogę uniknąć wielu pułapek bezpieczeństwa wątków. Jedna rzecz wciąż mnie prześladuje i jest to klasyczny przykład używany do nauczania bezpieczeństwa nici - wspólnego licznika.
Zastanawiałem się, czy możliwe byłoby zaimplementowanie licznika bezpiecznego dla wątków (w tym przykładzie licznika żądań), użycie niezmiennych obiektów i pojęć funkcjonalnych oraz całkowite uniknięcie synchronizacji.
Więc dla odniesienia tutaj są najpierw klasyczne, zmienne wersje licznika (przepraszam za zmienną członka publicznego, tylko dla zwięzłości przykładów)
Mutable, Non thread safe version:
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);
}
}
Zmutowana, klasyczna bezpieczna wersja nici: (a przynajmniej mam nadzieję ...)
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);
}
}
Zastanawiałem się, czy istnieje sposób wykorzystania niezmiennych obiektów i zmiennych zmiennych, aby osiągnąć bezpieczeństwo wątku bez synchronizacji.
Oto moja naiwna próba. Chodzi o to, aby mieć niezmienny obiekt dla licznika i po prostu zastąpić odniesienie do niego, używając zmiennej lotnej. Czuje się podejrzanie, ale warto spróbować.
Uchwyt:
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;
}
}
Zmodyfikowany serwlet:
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);
}
}
Mam silne przeczucie, że to również nie jest bezpieczne dla wątków, ponieważ czytam z inkrementatora i może uzyskać nieaktualną wartość (np. Jeśli odwołanie zostało już zastąpione innym wątkiem). W przypadku, gdy rzeczywiście nie jest bezpieczny dla wątków, zastanawiam się, czy w ogóle istnieje jakikolwiek „funkcjonalny” sposób na obsłużenie takiego scenariusza bez blokowania / synchronizacji.
Więc moje pytania są
Czy ten wątek jest bezpieczny przez przypadek?Jeśli tak, to dlaczego?Jeśli nie, czy istnieje jakikolwiek sposób na wdrożenie takiego licznika bez synchronizacji?Chociaż powyższy przykładowy kod znajduje się w Javie, odpowiedzi w Scali są oczywiście mile widziane