Reputation: 9781
in my project I have until now "synchronized" multiple threads (each running the same type of Runnable
) using a CyclicBarrier
. 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. 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
. Here's a short, compilable example. Any suggestions on how to improve it?
Upvotes: 3
Views: 5225
Reputation: 23455
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.
Upvotes: 0
Reputation: 15029
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). Secondly, you have just re-implemented CyclicBarrier mechanism, on which Java developers have spend time and effort to optimize it for best performance. Thirdly, CyclicBarrier uses ReentrantLock for synchronization, which is apparently more efficient and faster than using synchronized
keyword. 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. 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)?
Can you share a bit more about the purpose of the code and give a bit more detail?
Upvotes: 1
Reputation: 73625
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.
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.
Upvotes: 1
Reputation: 40256
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.
What went wrong with a CyclicBarrier/CountDownLatch? That seems like a much better solution.
Upvotes: 3