Java - 实现繁忙的等待机制

时间:2011-07-01 20:44:06

标签: java multithreading

在我的项目中,我一直使用CyclicBarrier“同步”多个线程(每个线程运行相同类型的Runnable)。就我而言,由于同步频率很高,使用CyclicBarrier效率低下,但繁忙等待机制可能会更快。这是我到目前为止所得到的(部分遗漏):

public class MyRunnable implements Runnable {

    private static AtomicInteger counter = null; // initialized to the number
                                                 // of threads

    public void run() {

        // do work up to a "common point"

        synchronized (this) {

            // decrement the counter and - if necessary - reset it
            if (counter.decrementAndGet() == 0) {

                counter.set(numberOfThreads);

                // make all the busy waiting threads exit from the loop
                for (int i = 0; i < threads.length; i++)
                    threads[i].interrupt();
            }
        }

        // busy wait until all threads have reached the "common point"
        while (!Thread.interrupted()) {}
    }
}

不幸的是,此代码的执行情况甚至比CyclicBarrier还差。 Here's一个简短的,可编辑的例子。有关如何改进它的任何建议吗?

4 个答案:

答案 0 :(得分:3)

如果你有更多的处理器然后运行线程,那么在这里忙碌等待只会'更快'。如果你继续使用Thread.interrupted并且只是消耗CPU时间,那么实际上会显着降低性能。

CyclicBarrier / CountDownLatch出了什么问题?这似乎是一个更好的解决方案。

答案 1 :(得分:1)

这样的事情怎么样?这段代码有一个并发错误(如果一个线程在调用counter.get()之间的速度很慢),但它应该可以解决,因为有两个计数器并重复这个代码两次,以便计数器交替。

if (counter.decrementAndGet() == 0) {
    counter.set(numberOfThreads);
} else {
    while (counter.get() < numberOfThreads) {}
}

请发布一个可以编译并演示性能问题的示例。否则所有答案都只是推测。

答案 2 :(得分:1)

很难想象繁忙等待循环会比非繁忙循环更快。首先,在您的代码中,使用CyclicBarrier时,您仍然需要使用同步(见下文)。其次,您刚刚重新实现了CyclicBarrier机制,Java开发人员花费时间和精力来优化它以获得最佳性能。第三,CyclicBarrier使用ReentrantLock进行同步,这显然比使用synchronized关键字更有效,更快。总的来说,你的代码不太可能赢得比赛。

考虑这个参考代码:

public class MyRunnable implements Runnable {

    private static CyclicBarrier barrier = new CyclicBarrier(threads.length);

    public void run() {

        // do work up to a "common point"

        try{
          barrier.await();
        }catch(InterruptedException e){
          Thread.interrupt();
          //Something unlikely has happened. You might want to handle this.
        }   
    }
 }

在一次运行中,此代码将同步thread.length次,这不会超过您的繁忙等待版本。因此它不能比你的代码慢。

性能问题的真正原因是你的线程在“遇到”之前几乎没有工作,这可能意味着存在高线程上下文切换开销以及大量同步。

你能重新考虑这个架构吗?你真的需要等待所有工人在共同点“见面”吗?你可以在他们的“会议”之前做更多的工作吗?您是否尝试将线程数设置为较小的数字(= CPU /核心数)?

您是否可以分享一些关于代码目的的更多信息并提供更多详细信息?

答案 3 :(得分:0)

等待/通知怎么办?

public class MyRunnable implements Runnable {

    private static AtomicInteger counter = null; // initialized to the number
                                                 // of threads

    public void run() {

        // do work up to a "common point"

        // need to synchronize for wait/notify.
        synchronized ( counter ) {
            // decrement the counter and - if necessary - reset it
            if (counter.decrementAndGet() == 0) {

                counter.set(numberOfThreads);

                // notify all the waiting threads
                counter.notifyAll();
            }else{
                // wait until all threads have reached the "common point"
                counter.wait();
            }
        }
    }
}

总的来说,如果你经常同步以至于屏障的开销是一个问题,那就很可疑:要么你做的工作不值得多线程,要么你的同步频率比你应该更频繁。