Как сделать отложенный неблокирующий вызов функции

Я хочу вызвать функцию add HashSet с некоторой задержкой, но без блокировки текущего потока. Есть ли простое решение для достижения чего-то вроде этого:

Utils.sleep(1000, myHashSet.add(foo)); //added after 1 second
//code here runs immediately without delay
...

person Thomas    schedule 04.06.2012    source источник
comment
Прямые ответы на ваш вопрос ниже. Но то, что они пытаются сделать, кажется довольно неестественным, что говорит о том, что, возможно, вам следует искать совершенно другое решение. Вы хотите предоставить больше контекста, почему вы хотите отложить добавление?   -  person Jochen    schedule 04.06.2012
comment
Я использую Storm для реализации сканера. URL-адреса для сканирования генерируются шаблоном, содержащим идентификатор потока и идентификатор доски. Природа сканера позволяет обрабатывать только один URL-адрес на доске в любой момент времени. Мой HashSet содержит все идентификаторы досок, которые в настоящее время доступны для сканирования. Сканирование одного URL-адреса может завершиться неудачно по разным причинам (тред был удален, 404,...). Некоторые причины позволяют повторить сканирование. Информация об этих причинах сохраняется в БД, которая не блокируется, поэтому должна быть некоторая задержка перед принятием решения о повторной попытке или нет.   -  person Thomas    schedule 04.06.2012
comment
Звучит излишне сложно. Почему сканирующие потоки не могут обработать возвращаемое значение и либо напрямую повторить попытку, если произошел устранимый сбой, либо, по крайней мере, добавить URL-адрес обратно на карту (для этого лучше использовать очередь).   -  person Jochen    schedule 04.06.2012
comment
Природа Storm немного усложняет задачу, обеспечивая легкое масштабирование и отказоустойчивость.   -  person Thomas    schedule 04.06.2012


Ответы (3)


Вы можете использовать ScheduledThreadPoolExecutor.schedule:

ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);

exec.schedule(new Runnable() {
          public void run() {
              myHashSet.add(foo);
          }
     }, 1, TimeUnit.SECONDS);

Он выполнит ваш код через 1 секунду в отдельном потоке. Однако будьте осторожны с параллельными модификациями myHashSet. Если вы одновременно изменяете коллекцию из другого потока или пытаетесь перебрать ее, у вас могут возникнуть проблемы, и вам придется использовать блокировки.

person Tudor    schedule 04.06.2012
comment
Не выполнять итерацию по набору во время операции добавления недостаточно, так как видимость изменения в памяти не гарантируется. - person Oz Molaim; 08.05.2018

Простое ванильное решение будет:

    new Thread( new Runnable() {
        public void run()  {
            try  { Thread.sleep( 1000 ); }
            catch (InterruptedException ie)  {}
            myHashSet.add( foo );
        }
    } ).start();

За кулисами здесь происходит намного меньше, чем с ThreadPoolExecutor. TPE может быть удобен для контроля количества потоков, но если вы запускаете много потоков, которые спят или ждут, ограничение их количества может снизить производительность гораздо больше, чем помочь.

И вы хотите синхронизироваться на myHashSet, если вы еще этого не сделали. Помните, что вам нужно синхронизировать везде, чтобы это принесло пользу. Есть и другие способы справиться с этим, например Collections.synchronizedMap или ConcurrentHashMap.

person RalphChapin    schedule 04.06.2012
comment
Thread.sleep() — это блокирующий вызов. Создание нового потока не будет блокировать основной поток, но все же в параллельном процессе он все равно будет блокировать поток. - person Pran Kumar Sarkar; 29.07.2021

Проверьте ThreadPoolExecutor.schedule().

person dbf    schedule 04.06.2012