用Java发出一个线程信号

时间:2013-09-13 14:09:46

标签: java linux multithreading

我有以下情况。我在我的Java应用程序(在Linux平台上)上运行了两个线程,线程一旦创建就会休眠。我希望在设置环境变量时唤醒线程。

我最初提出了让线程不断检查变量的想法,即忙碌的等待状态。但是因为它消耗了cpu周期,我知道它效率低下。因此,如果设置了环境变量,我想出了唤醒线程的想法。

那么有没有办法在Java中实现它? 提前谢谢。

4 个答案:

答案 0 :(得分:1)

我为此写了一个Doze课程。

它避免在内部使用Thread.sleep完全使用BlockingQueue。正如main方法所示,它使用起来很简单。您只是doze一段时间,如果有人调用Doze.wakeup()方法,您就会被唤醒。

您需要安排您的Doze对象可用于更新该属性的包。在更新时,它应调用其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();


  }
}

答案 1 :(得分:0)

此示例仅使用同步原语。如果使用阻塞数据结构等,模式可以更加灵活。

基本上,生成器线程将在设置系统属性时向使用者线程发出信号。然后,消费者线程将读取该属性。

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. */
        }                  
    }        
}

答案 2 :(得分:0)

这里有一些很好的答案,但我认为我会更简单。

  

我希望在设置环境变量时唤醒线程。

在查看您问题下方的评论时,您说您正在使用环境变量,以便同一程序的两个部分可以相互通知。同样重要的是要注意,像@Peter提到的那样,应用程序看不到在应用程序之外发生的环境变量更改。

但是就从一个线程向另一个线程发送信号而言,当您应该使用同步原语时,您正在使用“环境”变量。通常,两个线程共享一个锁对象。也许它需要公开才能被共享或传递到您的Runnable中,以便线程使用重要的相同的对象。

private final AtomicBoolean signal = new AtomicBoolean(false);

一个线程在该锁上调用wait()

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

该锁上的另一个线程调用notify()

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

我们在这里使用AtomicBoolean的原因是我们必须防范spurious wakeups。它们很少见,但在某些线程实现(或条件)下,wait()可以返回,而无需任何人直接调用对象上的notify()AtomicBoolean允许我们将wait()置于while(...)循环中以确保已达到条件 - 这是一个始终遵循的良好模式。

AtomicBoolean也允许我们预先通知。调用notify()的线程可能之前另一个线程在wait()内,在这种情况下,等待线程可能会永远休眠。对于AtomicBoolean,情况并非如此。

答案 3 :(得分:0)

您可以使用简单的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