0

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

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

那么有没有办法在Java中实现这一点?提前致谢。

4

4 回答 4

1

我为此写了一个Doze类。

它通过在内部Thread.sleep使用 a 来完全避免使用。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();


  }
}
于 2013-09-13T14:32:36.107 回答
1

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

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

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

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

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();
}

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

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

于 2013-09-13T14:47:00.957 回答
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
于 2013-09-13T17:35:24.340 回答
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. */
        }                  
    }        
}
于 2013-09-13T14:34:57.093 回答