我有以下情况。我有两个线程在我的 Java 应用程序(在 Linux 平台上)中运行,线程在创建后立即休眠。我希望线程在设置环境变量时唤醒。
我最初提出了让线程不断检查变量的想法,即像一个忙碌的等待状态。但由于它消耗 cpu 周期,我知道它效率低下。所以我想出了如果设置了环境变量就唤醒线程的想法。
那么有没有办法在Java中实现这一点?提前致谢。
我有以下情况。我有两个线程在我的 Java 应用程序(在 Linux 平台上)中运行,线程在创建后立即休眠。我希望线程在设置环境变量时唤醒。
我最初提出了让线程不断检查变量的想法,即像一个忙碌的等待状态。但由于它消耗 cpu 周期,我知道它效率低下。所以我想出了如果设置了环境变量就唤醒线程的想法。
那么有没有办法在Java中实现这一点?提前致谢。
我为此写了一个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();
}
}
这里有一些很好的答案,但我认为我会更简单。
我希望线程在设置环境变量时唤醒。
在查看问题下方的评论时,您说您正在使用环境变量,以便同一程序的两个部分可以相互通知。同样重要的是要注意,就像@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
并非如此。
你可以使用一个简单的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
此示例仅使用同步原语。如果您使用阻塞数据结构等,该模式可以更加灵活。
基本上,当设置系统属性时,生产者线程将向消费者线程发出信号。然后消费者线程将读取该属性。
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. */
}
}
}