Как да направите отложено извикване на неблокираща функция

Искам да извикам функцията 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