11

I want to call the add function of an HashSet with some delay, but without blocking the current thread. Is there an easy solution to achieve something like this:

Utils.sleep(1000, myHashSet.add(foo)); //added after 1 second
//code here runs immediately without delay
...
Tudor
  • 61,523
  • 12
  • 102
  • 142
Thomas
  • 10,289
  • 13
  • 39
  • 55
  • 1
    The direct answers to your question are below. But what are are trying to do seems rather unnatural, which suggests that maybe you should look for an entirely different solution. Do you want to provide some more context why you want to delay the add? – Jochen Jun 04 '12 at 14:05
  • I am using [Storm](https://github.com/nathanmarz/storm) to implement a crawler. URLs to crawl are generated by a pattern containing a thread id and a board id. The nature of the crawler allows only one url per board to be processed at any time. My HashSet contains all the ids of boards that are currently free to crawl. The crawling of a single url can fail for different reasons (thread got deleted, 404,...). Some reasons allow to retry the crawling. The information about these reasons is saved in a DB which does not lock, so there should be some delay before deciding either to retry or not to. – Thomas Jun 04 '12 at 14:23
  • That sounds unnecessarily complicated. Why can't the crawling threads process the return value and either directly retry when there was a recoverable failure, or at least add the URL back into the map (a queue might be better for this). – Jochen Jun 04 '12 at 14:31
  • The nature of Storm makes this a little more complicated, with the gain of easy scaling and fault-tolerance. – Thomas Jun 04 '12 at 14:33

3 Answers3

14

You can use ScheduledThreadPoolExecutor.schedule:

ScheduledThreadPoolExecutor exec = new ScheduledThreadPoolExecutor(1);

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

It will execute your code after 1 second on a separate thread. Be careful about concurrent modifications of myHashSet though. If you are modifying the collection at the same time from a different thread or trying to iterate over it you may be in trouble and will need to use locks.

Tudor
  • 61,523
  • 12
  • 102
  • 142
  • Not iterating over the set during the add operation is not enough since memory visibility of the change is not guaranteed. – Oz Molaim May 08 '18 at 07:15
12

The plain vanilla solution would be:

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

There's a lot less going on behind the scenes here than with ThreadPoolExecutor. TPE can be handy to keep the number of threads under control, but if you're spinning off a lot of threads that sleep or wait, limiting their number may hurt performance a lot more than it helps.

And you want to synchronize on myHashSet if you haven't handled this already. Remember that you have to synchronize everywhere for this to do any good. There are other ways to handle this, like Collections.synchronizedMap or ConcurrentHashMap.

RalphChapin
  • 3,108
  • 16
  • 18
  • 3
    Thread.sleep() is a blocking call. Creating a new thread will not block the main thread, but still in parallel process its still going to block a thread. – Pran Kumar Sarkar Jul 29 '21 at 20:01
0

Check ThreadPoolExecutor.schedule() method.

Eugene Kuleshov
  • 31,461
  • 5
  • 66
  • 67
dbf
  • 6,399
  • 2
  • 38
  • 65