Правильный ответ - это, я считаю, создание Runnable для выполнения подпрограммы и запуск этого в отдельном потоке. Runnable может быть FutureTask, который можно запустить с помощью тайм-аута (метод get). Если он истечет, вы получите исключение TimeoutException, в котором я предлагаю вам
В моем приложении, где я запускаю ненадежный, неконтактный код, написанный моим начинающих учеников, я делаю выше, гарантируя, что убитый поток никогда не имеет (не пишет) доступа к любым объектам, которые пережили его смерть. Сюда входит объект, в котором размещается вызываемый метод, который отбрасывается, если происходит таймаут. (Я говорю своим ученикам, чтобы избежать тайм-аутов, потому что их агент будет дисквалифицирован.) Я не уверен в утечке памяти ...
Я различаю длительные промежутки времени (метод завершает) и жесткие таймауты - жесткие таймауты
Из моих исследований, Java, похоже, не имеет устаревшего положения для запуска несовместимого кода, что в некотором смысле является зияющей дырой в модели безопасности. Либо я могу запускать внешний код и управлять правами, которые он имеет (SecurityManager), либо я не могу запускать внешний код, потому что он может в конечном итоге заняться целым ЦП без каких-либо устаревших средств для его остановки.
double x = 2.0;
while(true) {x = x*x}; // do not terminate
System.out.print(x); // prevent optimization