Pracuję nad aplikacją, która uzyskuje dostęp do bazy danych SQLite. Problem polega na tym, że DB zostaje zablokowany, gdy pojawia się zapytanie do niego. Zazwyczaj nie stanowi to problemu, ponieważ przepływ aplikacji jest dość liniowy.Zarządzanie wątkami uzyskującymi dostęp do bazy danych za pomocą Java
Jednak mam bardzo długi proces obliczania, który jest uruchamiany przez użytkownika. Ten proces obejmuje wielokrotne wywołania do bazy danych pomiędzy obliczeniami.
Chciałem, aby użytkownik otrzymał wizualną informację zwrotną, więc korzystałem z Javibx progressIndicator i usługi ze struktury Javafx.Concurrency. Problem polega na tym, że użytkownik może swobodnie poruszać się po aplikacji i potencjalnie uruchamiać inne połączenia z bazą danych.
Spowodowało to wyjątek, że plik bazy danych jest zablokowany. Chciałbym sposób, aby zatrzymać ten wątek z uruchomieniem, gdy ten przypadek się dzieje, ale nie udało mi się znaleźć żadnych wyraźnych przykładów w Internecie. Większość z nich jest uproszczona i chciałbym, aby metoda była skalowalna. Próbowałem już użyć metody cancel(), ale to nie gwarantuje, że wątek zostanie anulowany na czas.
Ponieważ nie jestem w stanie sprawdzić wszystkich części kodu w przypadku isCancelled, czasami występuje opóźnienie między momentem anulowania wątku a czasem jego skutecznego zatrzymania.
Więc myślałem o następującym rozwiązaniu, ale chciałbym wiedzieć, czy istnieje lepszy sposób pod względem wydajności i uniknięcia warunków wyścigowych i zawieszenia.
// Start service
final CalculatorService calculatorService = new CalculatorService();
// Register service with thread manager
threadManager.registerService(CalculatorService);
// Show the progress indicator only when the service is running
progressIndicator.visibleProperty().bind(calculatorService.runningProperty());
calculatorService.setOnSucceeded(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent workerStateEvent) {
System.out.println("SUCCEEDED");
calculatorService.setStopped(true);
}
});
// If something goes wrong display message
calculatorService.setOnFailed(new EventHandler<WorkerStateEvent>() {
@Override
public void handle(WorkerStateEvent workerStateEvent) {
System.out.println("FAILED");
calculatorService.setStopped(true);
}
});
// Restart the service
calculatorService.restart();
To jest moje klasy usługi, które mam podklasy zawierać metody, które mogą być wykorzystane, aby ustawić stan usługi (zatrzymany lub nie zatrzymał się)
public class CalculatorService extends Service implements CustomService {
private AtomicBoolean stopped;
private CalculatorService serviceInstance;
public FindBundleService() {
stopped = new AtomicBoolean(false);
instance = this;
}
@Override
protected Task<Results> createTask() {
return new Task<Result>() {
@Override
protected Result call() throws Exception {
try {
Result = calculationMethod(this, serviceInstance);
return Result;
} catch (Exception ex) {
// If the thread is interrupted return
setStopped(true);
return null;
}
}
};
}
@Override
public boolean isStopped() {
return stopped.get();
}
@Override
public void setStopped(boolean stopped) {
this.stopped.set(stopped);
}
}
Usługa implementuje ten interfejs, który ja zdefiniowano:
public interface CustomService {
/**
* Method to check if a service has been stopped
*
* @return
*/
public boolean isStopped();
/**
* Method to set a service as stopped
*
* @param stopped
*/
public void setStopped(boolean stopped);
}
Wszystkie usługi muszą zarejestrować się w menedżerze wątków, który jest pojedynczą klasą.
public class ThreadManager {
private ArrayList<CustomService> services;
/**
* Constructor
*/
public ThreadManager() {
services = new ArrayList<CustomService>();
}
/**
* Method to cancel running services
*/
public boolean cancelServices() {
for(CustomService service : services) {
if(service.isRunning()) {
((Service) service).cancel();
while(!service.isStopped()) {
// Wait for it to stop
}
}
}
return true;
}
/**
* Method to register a service
*/
public void registerService(CustomService service) {
services.add(service);
}
/**
* Method to remove a service
*/
public void removeService(CustomService service) {
services.remove(service);
}
}
W dowolnym miejscu w aplikacji, jeśli chcemy zatrzymać usługę, wywołujemy cancelServices(). To ustawi stan na anulowany Sprawdzam to w moim calculethMethod(), a następnie ustawiam stan, aby zatrzymał się tuż przed powrotem (skutecznie kończąc wątek).
if(task.isCancelled()) {
service.setStopped(true);
return null;
}
Dlaczego wielokrotne wywołania db? Dlaczego nie uzyskać wszystkich potrzebnych informacji w jednym połączeniu? – Sedrick
To dlatego, że robimy pewne obliczenia na podstawie tego, co otrzymaliśmy od DB, a następnie używając wyników, aby uzyskać inne rzeczy z DB. Jest to logika biznesowa, której nie mogę zmienić. – user3552551
Chciałbym wyłączyć wszystkie 'węzły', które wyzwalałyby inne wywołanie db, dopóki nie zakończy się' task'. – Sedrick