安全地暂停和恢复线程

时间:2014-10-31 16:22:01

标签: java thread-safety

我想创建一个线程来每隔几秒发出一些HTTP请求,并且很容易暂时停止并立即恢复。

以下方式是首选,安全和有效吗?

public class Facebook extends Thread {

    public boolean running = false;

    public void startThread() {
        running = true;
    }

    public void stopThread() {
        running = false;
    }

    public void run() {
        while(true) {
            while(running) {
                //HTTP Calls
                Facebook.sleep(2000);
            }
        }
    }

}

3 个答案:

答案 0 :(得分:3)

您的代码:

在您的示例中,boolean应该volatile boolean才能正常运行。另一个问题是如果running == false你的线程只是在紧密的循环中烧掉CPU,你可能会想要使用对象监视器或Condition实际上等待标志成为true试。

计时器选项:

我建议只为此创建一个Timer。每个Timer都隐式获得自己的线程,这就是您要完成的任务。

然后创建一个TimerTask(下面是FacebookTask这个)来执行你的任务,并且从你的主控制类中,不需要显式线程,如:

Timer t;

void resumeRequests () {
    if (t == null) { // otherwise its already running
        t = new Timer();
        t.scheduleAtFixedRate(new FacebookTask(), 0, 2000);
    }
}

void pauseRequests () {
    if (t != null) { // otherwise its not running
        t.cancel();
        t = null;
    }
}

注意,在上面,resumeRequests()将导致请求在恢复后立即发生(由0 delay参数指定);理论上,如果你在不到2000毫秒的时间内暂停和重复恢复请求率。这对您来说似乎不是一个问题;但另一种实现方法是让计时器持续运行,并在volatile bool中设置一个FacebookTask标志,您可以将其设置为启用/禁用它(所以如果它是false则不会发出请求,但每隔2000毫秒继续检查一次。选择最合适的选择。

其他选项:

您还可以在评论中使用scheduled executor service作为fge提及。它具有比计时器更多的功能,同样易于使用;如果您将来需要添加更多任务,它们也会很好地扩展。

在任何情况下都没有真正的理由直接打扰Thread s; JDK中有很多很棒的工具可供这项工作使用。

答案 1 :(得分:2)

使用Timer的建议会更好。但是,如果你想手动进行线程化,那么更像这样的东西会更安全,更好:

class Facebook implements Runnable {

    private final Object monitor = new Object();

    public boolean running = false;

    public void startThread() {
        synchronized (monitor) {
            running = true;
            monitor.notifyAll();
        }
    }

    public void stopThread() {
        synchronized (monitor) {
            running = false;
        }
    }

    @Override
    public void run() {
        while(true) {
            try {
                synchronized (monitor) {
                    // Wait until somebody calls startThread()
                    while (!running) {
                        monitor.wait();
                    }
                }

                //HTTP Calls
                Thread.sleep(2000);
            } catch (InterruptedException ie) {
                break;
            }
        }
    }
}

特别注意:

  1. 您通常应该实现Runnable而不是子类Thread,然后使用Runnable来指定通用Thread的工作。线程执行的工作与线程本身不同,因此这会产生更好的模型。如果您希望能够通过其他方式执行相同的工作(例如Timer),它也会更灵活。
  2. 只要您希望两个线程交换数据(例如running实例变量的状态),您就需要使用某种形式的同步。例如,有一些类AtomicBoolean内置了这种同步,但有时手动同步也有优势。
  3. 在特定情况下,您希望一个线程停止工作,直到另一个线程指示它继续,您通常要使用Object.wait()和相应的Object.notify()Object.notifyAll(),如演示以上。等待线程消耗零CPU,直到发出信号。由于您无论如何都需要使用wait / notify手动同步,因此使用AtomicBoolean无法获得额外的优势。
  4. 已编辑添加:

    由于显然有一些关于如何使用它(或原始版本,我猜)的混淆,这是一个例子:

    class MyClass {
        static void main(String[] args) {
            FaceBook fb = new FaceBook();
            Thread fbThread = new Thread(fb);
    
            fbThread.start();
    
            /* ... do stuff ... */
    
            // Pause the FaceBook thread:
            fb.stopThread();
    
            /* ... do more stuff ... */
    
            // Resume the FaceBook thread:
            fb.startThread();
    
            // etc.
    
            // When done:
            fbThread.interrupt(); // else the program never exits
        }
    }
    

答案 2 :(得分:0)

我建议你使用a guarded blocks并将线程附加到计时器