我有以下情况。我在我的Java应用程序(在Linux平台上)上运行了两个线程,线程一旦创建就会休眠。我希望在设置环境变量时唤醒线程。
我最初提出了让线程不断检查变量的想法,即忙碌的等待状态。但是因为它消耗了cpu周期,我知道它效率低下。因此,如果设置了环境变量,我想出了唤醒线程的想法。
那么有没有办法在Java中实现它? 提前谢谢。
答案 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