0

I have the following scenario. I have two threads running in my Java application ( on Linux platform ) and the threads sleep as soon as they are created. I want the threads to wake up when an environment variable is set.

I initially came up with the idea of making the thread continuously checking the variable, i.e. like a busy waiting state. But as it consume cpu cycles I know it is inefficient. So I came up with the idea of waking up a thread if an environment variable is set.

So is there a way to implement this in Java? Thanks in advance.

shar
  • 1,988
  • 2
  • 18
  • 25

4 Answers4

1

I wrote myself a Doze class for this.

It avoids using Thread.sleep completely by using a BlockingQueue internally. It is simple to use, as the main method shows. You just doze for a time and if anyone calls the Doze.wakeup() method you get woken up.

You will need to arrange for your Doze object to be available to the package that updates the property. On update it should call its wakeup().

/**
 * Use one of these to doze for a certain time.
 *
 * The dozing is fully interruptable.
 *
 * Another thread can stop the caller's doze with either a wakeup call or an abort call.
 *
 * These can be interpreted in any way you like but it is intended that a Wakeup is
 * interpreted as a normal awakening and should probably be treated in exactly the
 * same way as an Alarm. An Abort should probably be interpreted as a suggestion
 * to abandon the proces.
 */
public class Doze {
  // Special alarm messages.
  public enum Alarm {
    // Standard timeout.
    Alarm,
    // Just wake from your doze.
    Wakeup,
    // Abort the whole Doze process.
    Abort;
  }
  // My queue to wait on.
  private final ArrayBlockingQueue<Alarm> doze = new ArrayBlockingQueue<>(1);
  // How long to wait by default.
  private final long wait;

  public Doze(long wait) {
    this.wait = wait;
  }

  public Doze() {
    this(0);
  }

  public Alarm doze() throws InterruptedException {
    // Wait that long.
    return doze(wait);
  }

  public Alarm doze(long wait) throws InterruptedException {
    // Wait that long.
    Alarm poll = doze.poll(wait, TimeUnit.MILLISECONDS);
    // If we got nothing then it must be a normal wakeup.
    return poll == null ? Alarm.Alarm : poll;
  }

  public void wakeup() {
    // Just post a Wakeup.
    doze.add(Alarm.Wakeup);
  }

  public void abort() {
    // Signal the system to abort.
    doze.add(Alarm.Abort);
  }

  // Demo of use.
  public static void main(String[] args) throws InterruptedException {
    // Doze for 1 second.
    final Doze d = new Doze(1 * 1000);

    // Start a dozing thread.
    new Thread(new Runnable() {
      @Override
      public void run() {
        try {
          Alarm a = d.doze();
          // Wait forever until we are aborted.
          while (a != Alarm.Abort) {
            System.out.println("Doze returned " + a);
            a = d.doze();
          }
          System.out.println("Doze returned " + a);
        } catch (InterruptedException ex) {
          // Just exit on interrupt.
        }
      }
    }).start();

    // Wait for a few seconds.
    Thread.sleep(3000);

    // Wake it up.
    d.wakeup();

    // Wait for a few seconds.
    Thread.sleep(3000);

    // Abort it.
    d.abort();


  }
}
OldCurmudgeon
  • 64,482
  • 16
  • 119
  • 213
1

There are some good answers here but I thought I'd be more simple.

I want the threads to wake up when an environment variable is set.

In looking at the comments below your question, you say that you are using environmental variables so that two parts of the same program can notify each other. It is also important to note that like @Peter mentions, environmental variable changes that happen outside of an application are not seen by the application.

But in terms of sending a signal from one thread to another, you are using an "environment" variable when you should be using a synchronization primitive. Typically two threads share a lock object. Maybe it needs to be public to be shared or passed into your Runnables for the threads to use the same object which is important.

private final AtomicBoolean signal = new AtomicBoolean(false);

One thread calls wait() on that lock:

while (!signal.get()) {
    synchronized (signal) {
        signal.wait();
    }
}
signal.set(false);

Another thread call notify() on that lock:

synchronized (signal) {
     signal.set(true);
     signal.notify();
}

The reason why we use an AtomicBoolean here is that we have to protect against spurious wakeups. They are rare but under certain thread implementations (or conditions) the wait() could return without anyone directly calling the notify() on the object. The AtomicBoolean allows us to put the wait() in a while(...) loop to ensure that the condition has been reached -- this is a good pattern to follow always.

The AtomicBoolean also allows us to pre-notify. The thread that calls notify() may do so before the other thread is inside of wait() in which case the waiting thread may sleep forever. With the AtomicBoolean this is not the case.

Gray
  • 115,027
  • 24
  • 293
  • 354
  • “spurious wakeups” are not architecture-specific. Besides architectur spefic reasons they come also from the fact that multiple events can collapse into a single signal. The receiver therefore has to check for all pending events after wakeup not just a single one. But then it might process concurrent events whose signal is still pending; in this case the next wait will “spuriously” wakeup. Using a single boolean value does not help against this as all attempts to set it to `true` are also collapsed to a single value. – Holger Sep 13 '13 at 15:20
  • According to what I've read "spurious wakeups" _are_ more common depending on the thread implementation. The `AtomicBoolean` certainly _does_ protect against spurious wakeups by allowing the `while` loop to spin to make sure the actual condition is reached. – Gray Sep 13 '13 at 15:33
  • Per the wikipedia article it talks about pthreads and windows APIs as examples of architectures that give spurious wakeups. Please supply a link to a page that says they are not arch dependent: https://en.wikipedia.org/wiki/Spurious_wakeup – Gray Sep 13 '13 at 15:37
  • “by allowing the while loop to spin to make sure the actual condition is reached” only if having a value of `true` in your atomic boolean *is* the condition you are talking about. But that’s not the case in a real application. The question does not even mention an atomic boolean. I did not say that there are no architecture specific reasons at all. I explained why spurious wakeups will still be visible if there were no architecture specific reasons at all. Try to understand instead of asking for another webpage telling you the same. – Holger Sep 13 '13 at 15:54
  • By the way the Wikipedia article you linked list in the section “Other reasons for verifying the invariant” another good reason why your atomic boolean does not save the programmer from checking the *actual* condition (s)he is waiting for. – Holger Sep 13 '13 at 15:57
  • Sigh. I am defining the `AtomicBoolean` in my answer dude -- it's not in the question. I'm trying to explain how to send a single from one thread to another. I am using an `AtomicBoolean` to protect against spurious wakeups so the signal is set to true _and_ notified. – Gray Sep 13 '13 at 15:58
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/37348/discussion-between-gray-and-holger) – Gray Sep 13 '13 at 15:59
  • The question was about environmental variables. So the application *has* to check whether real variable changes occurred or not. Regardless of your atomic boolean. That boolean is just an obsolete addition. – Holger Sep 13 '13 at 16:00
  • Read the comments below the question. The guy is talking about two parts of the same program signaling each other. I'll make that more clear in my answer. – Gray Sep 13 '13 at 16:02
0

This example uses only synchronization primitives. The pattern can be far more flexible if you use blocking data structures and the like.

Basically the producer thread will signal the consumer thread when a system property has been set. The consumer thread will then read that property.

package com.stackoverflow._18788457;

public class ProducerConsumer {

    public static void main(String[] args) throws Exception {
        Object lock = new Object();
        Thread cthread = new Thread(new Consumer(lock));
        Thread pthread = new Thread(new Producer(lock));

        cthread.start();

        /* cthread will wait for pthread with the shared lock object. */
        Thread.sleep(3000);

        pthread.start();
    }     
}

class Producer implements Runnable {

    private final String sysprop_value = "value";
    private final Object lock;

    public Producer(Object lock) {
        this.lock = lock;
    }

    @Override
    public void run() {
        synchronized(lock){
            System.setProperty(Consumer.SYSPROP_NAME, sysprop_value);
            System.out.println("Producer: Set system property. Notifying waiters.");
            lock.notifyAll();
        }
    }
}

class Consumer implements Runnable {

    public final static String SYSPROP_NAME = "MYENVVAR";
    private final Object lock;

    public Consumer(Object lock){
        this.lock = lock;
    }

    @Override
    public void run() {
        String var = null;
        synchronized(lock){
            try {
                while((var = System.getProperty(SYSPROP_NAME)) == null){
                        System.out.println("Consumer: Waiting for system property...");
                        lock.wait();  
                }
                System.out.println("Consumer: Acquired system property.");

            } catch (InterruptedException e) {
                /* Do something appropriate.*/
                System.out.println("Consumer: Interrupted!");
                Thread.currentThread().interrupt(); //Reset interrupt.

            }
        }
        /* Check var is valid, the wait may have been interrupted. */
        if(var != null){
            System.out.println("Consumer: System property value: " + var);
            /* Do something with var. */
        }                  
    }        
}
Dev
  • 11,919
  • 3
  • 40
  • 53
0

You could use a simple CountDownLatch:

private final CountDownLatch latch = new CountDownLatch(1);

//in the threads that need to wait
latch.await(); //instead of sleep

//in the thread that sets the variable:
setVariable();
latch.countDown(); //wakes up the waiting threads
assylias
  • 321,522
  • 82
  • 660
  • 783