简体   繁体   English

Java - 实现忙等待机制

[英]Java - implementing a busy wait mechanism

in my project I have until now "synchronized" multiple threads (each running the same type of Runnable ) using a CyclicBarrier .在我的项目中,到目前为止,我使用CyclicBarrier “同步”了多个线程(每个线程都运行相同类型的Runnable )。 In my case, using a CyclicBarrier turned out to be inefficient due to the high frequency of synchronizations, but a busy-wait mechanism might work faster.在我的情况下,由于同步频率很高,使用CyclicBarrier效率低下,但忙碌等待机制可能会更快。 Here's what I got so far (some parts left out):这是我到目前为止得到的(有些部分遗漏了):

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()) {}
    }
}

Unfortunately, this code performs even worse than CyclicBarrier .不幸的是,这段代码的性能甚至比CyclicBarrier还要差。 Here's a short, compilable example.这是一个简短的可编译示例。 Any suggestions on how to improve it?关于如何改进它的任何建议?

Busy wait here will only work 'faster' if you have more processors then you have threads running.如果你有更多的处理器然后你有线程在运行,那么这里的忙碌等待只会“更快”地工作。 If you continuously spin on Thread.interrupted and are just consuming CPU time you will actually degrade performance dramatically.如果你不断地在 Thread.interrupted 上旋转并且只是在消耗 CPU 时间,你实际上会显着降低性能。

What went wrong with a CyclicBarrier/CountDownLatch? CyclicBarrier/CountDownLatch 出了什么问题? That seems like a much better solution.这似乎是一个更好的解决方案。

How about something like this?这样的事情怎么样? This code has a concurrency bug (if one thread is slow between calls to counter.get() ), but it should be solvable by having two counters and repeating this code twice so that the counters alternate.这段代码有一个并发错误(如果一个线程在调用counter.get()之间很慢),但它应该可以通过有两个计数器并重复此代码两次以使计数器交替来解决。

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

Please post an example which can be compiled and which demonstrates the performance problem.请发布一个可以编译并演示性能问题的示例。 Otherwise all answers will be just speculation.否则所有的答案都只是猜测。

It's hard to imagine that busy-wait loop would be faster than non busy one.很难想象忙等待循环会比非忙循环更快。 First of all, in your code you are still using no less synchronization that you would need when using a CyclicBarrier (see below).首先,在您的代码中,您使用的同步功能仍然不亚于使用 CyclicBarrier 时所需的同步功能(见下文)。 Secondly, you have just re-implemented CyclicBarrier mechanism, on which Java developers have spend time and effort to optimize it for best performance.其次,您刚刚重新实现了 CyclicBarrier 机制,Java 开发人员已经花费时间和精力对其进行优化以获得最佳性能。 Thirdly, CyclicBarrier uses ReentrantLock for synchronization, which is apparently more efficient and faster than using synchronized keyword.第三,CyclicBarrier 使用ReentrantLock进行同步,这显然比使用synchronized关键字更高效、更快。 So overall it's unlikely your code will win the race.所以总的来说,你的代码不太可能赢得比赛。

Consider this reference code:考虑这个参考代码:

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.
        }   
    }
 }

In one run, this code will synchronize thread.length times, which is no more than in your version with busy-wait.在一次运行中,此代码将同步thread.length时间,这不超过您的忙等待版本。 Therefore it cannot be slower than your code.因此它不能比您的代码慢。

The real cause of performance problem is that your threads do little work before they "meet", which probably means there is high thread context switch overhead as well as a lot of synchronization.性能问题的真正原因是您的线程在“相遇”之前几乎没有做任何工作,这可能意味着线程上下文切换开销很高以及同步很多。

Can you re-consider the architecture?你能重新考虑一下架构吗? Do you really need to wait for all workers to "meet" at common point?你真的需要等待所有工人在共同点“见面”吗? Can you do a bit more work before their "meeting"?你能在他们“见面”之前做更多的工作吗? Have you tried setting thread count to a smaller number (=CPU/core count)?您是否尝试将线程数设置为较小的数字(= CPU/核心数)?

Can you share a bit more about the purpose of the code and give a bit more detail?你能分享更多关于代码的目的并提供更多细节吗?

What about wait/notify?等待/通知呢?

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();
            }
        }
    }
}

On a general note, if you're synchronizing so often that the barrier's overhead is a problem, it is suspicious: either you're doing work that's not worth multithreading, or you're synchronizing more often than you should be.一般来说,如果你同步的频率太高以至于屏障的开销是个问题,那就很可疑了:要么你正在做不值得多线程的工作,要么你的同步频率超出了应有的水平。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM