Search code examples
javamultithreadingexecutorserviceshutdown

Java ExecutorService shutdownNow not interrupting


I'm having issues shutting down executor service in my password cracker assignment. I'm aware that shutdownNoW() does not have any guarantee and only tries its best by sending interruption to all threads. I have tried putting Thread.currentThread.isInterrupted() here and there but something is still missing. I have checked and it stops checking new possibilities and CPU performance drops suddenly as well but the app still continues to run. What am I missing?

public void decrypt() {
    setStartTime();
    generateTasks();
    for (Callable<String> task : tasks) {
        Future<String> future = executor.submit(task);
        futureList.add(future);
    }
    for (Future<String> future : futureList) {
        try {
            if (future.get() != null) {
                executor.shutdownNow();
                decryptedPassword = future.get();
                runTime = System.currentTimeMillis() - startTime;
                printResult();
            }
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

public void generateTasks() {
    for (int i = 0; i < maxPasswordLength; i++) {
        for (char ch = 'a'; ch <= 'z'; ch++) {
            tasks.add(new GuessGroup(ch, i, possibleChars, encryptedPassword));
        }
    }
}

My GuessGroup class implementing Callable:

@Override
public String call() throws Exception {
    for (int i = 0; i < passwordLength; i++) {
        String result = recursiveCrack("", i);
        if (result != null) {
            return result;
        }
        else if (Thread.currentThread().isInterrupted()) {
            return null;
        }
    }
    return null;
}

public String recursiveCrack(String stem, int length) {
    if (Thread.currentThread().isInterrupted()) {
        return null;
    }
    if (length == 0) {
        if (hashCalc.hash(prefix + stem).equals(encryptedPassword)) {
            String result = prefix + stem;
            return result;
        }
    }
    else {
        for (char ch : possibleChars) {
            String result = recursiveCrack(stem + ch, length - 1);
            if (result != null) {
                return result;
            }
        }
    }
    return null;
}

Solution

  • Found the issue, it was a very silly one... I added a break into the for loop which iterated through the Future list after the shutdownNow request and it fixed it.

    for (Future<String> future : futureList) {
        try {
            if (future.get() != null) {
                executor.shutdownNow();
                decryptedPassword = future.get();
                runTime = System.currentTimeMillis() - startTime;
                printResult();
                break;              // this was missing...
            }
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
    

    All the threads were down already and only main was still running.