两个线程完成执行后清理

时间:2015-09-15 15:24:52

标签: java multithreading synchronization

我有一个运行作业的应用程序,每个作业需要两个线程。这两个线程通常会做一些工作并且很快就会完成。然后在第二个线程完成之后我需要做一些清理,但由于线程正在做一些网络IO,因此一个线程可能会被阻塞很长时间。在这种情况下,我希望在第一个线程完成后几秒钟进行清理。

我在回调类中使用以下代码实现了此行为:

private boolean first = true;

public synchronized void done() throws InterruptedException {
    if (first) {
        first = false;
        wait(3000);
        // cleanup here, as soon as possible
    }
    else {
        notify();
    }
}

两个线程在完成时都会调用done()方法。然后第一个将在wait()中阻塞最多3秒,但是当秒线程调用done()方法时将立即通知。

我已经测试了这个实现,它似乎运行良好,但我很好奇是否有更好的方法来做到这一点。虽然这个实现看起来并不太复杂,但我担心我的程序会死锁或者有一些意想不到的同步问题。

2 个答案:

答案 0 :(得分:0)

由于done方法是同步的,所以一次只能执行一个线程,第二个将等待发送notify直到第一次完成整个作业,这可能会导致性能瓶颈。

我宁愿使用短同步块进行设计,主要更新boolean first

答案 1 :(得分:0)

我希望我理解你的需要。你想等待thread-a完成然后等待3秒或者等待thread-b结束。

最好使用较新的Concurrent工具而不是旧版wait/notify,因为它们有很多边缘情况。

// Two threads running so count down from 2.
CountDownLatch wait = new CountDownLatch(2);

class TestRun implements Runnable {

    private final long waitTime;

    public TestRun(long waitTime) {
        this.waitTime = waitTime;
    }

    @Override
    public void run() {
        try {
            // Wait a few seconds.
            Thread.sleep(waitTime);
            // Finished! Count me down.
            wait.countDown();
            System.out.println(new Date() + ": " + Thread.currentThread().getName() + " - Finished");
        } catch (InterruptedException ex) {
            System.out.println(Thread.currentThread().getName() + " - Interrupted");
        }
    }

}

public void test() throws InterruptedException {
    // ThreadA
    Thread threadA = new Thread(new TestRun(10000), "Thread A");
    // ThreadB
    Thread threadB = new Thread(new TestRun(30000), "Thread B");
    // Fire them up.
    threadA.start();
    threadB.start();
    // Wait for all to finish but threadA must finish.
    threadA.join();
    // Wait up to 3 seconds for B.
    wait.await(3, TimeUnit.SECONDS);
    System.out.println(new Date() + ": Done");
    threadB.join();
}

愉快地打印:

Tue Sep 15 16:59:37 BST 2015: Thread A - Finished
Tue Sep 15 16:59:40 BST 2015: Done
Tue Sep 15 16:59:57 BST 2015: Thread B - Finished

<强>加

随着新的清晰度 - 任何线程的结束启动计时器 - 我们可以使用第三个线程进行清理。每个线程在完成时都必须调用一个方法来触发清理机制。

// Two threads running so count down from 2.
CountDownLatch wait = new CountDownLatch(2);

class TestRun implements Runnable {

    private final long waitTime;

    public TestRun(long waitTime) {
        this.waitTime = waitTime;
    }

    @Override
    public void run() {
        try {
            // Wait a few seconds.
            Thread.sleep(waitTime);
            // Finished! Count me down.
            wait.countDown();
            System.out.println(new Date() + ": " + Thread.currentThread().getName() + " - Finished");
            // Record that I've finished.
            finished();
        } catch (InterruptedException ex) {
            System.out.println(Thread.currentThread().getName() + " - Interrupted");
        }
    }

}

Runnable cleanup = new Runnable() {

    @Override
    public void run() {
        try {
            // Wait up to 3 seconds for both threads to clear.
            wait.await(3, TimeUnit.SECONDS);
            // Do your cleanup stuff here.
            // ...
            System.out.println(new Date() + ": " + Thread.currentThread().getName() + " - Finished");
        } catch (InterruptedException ex) {
            System.out.println(Thread.currentThread().getName() + " - Interrupted");
        }
    }

};

final AtomicBoolean cleanupStarted = new AtomicBoolean(false);

private void finished() {
    // Make sure I only start the cleanup once.
    if (cleanupStarted.compareAndSet(false, true)) {
        new Thread(cleanup, "Cleanup").start();
    }
}

public void test() throws InterruptedException {
    // ThreadA
    Thread threadA = new Thread(new TestRun(10000), "Thread A");
    // ThreadB
    Thread threadB = new Thread(new TestRun(30000), "Thread B");
    // Fire them up.
    threadA.start();
    threadB.start();
    System.out.println(new Date() + ": Done");
}