I am thinking about implementing a lock free circular array. One problem is maintaining the head and tail pointers in a lock free manner. The code I have in mind is:
int circularIncrementAndGet(AtomicInteger i) {
i.compareAndSet(array.length - 1, -1);
return i.incrementAndGet();
}
Then I would do something like:
void add(double value) {
int idx = circularIncrementAndGet(tail);
array[idx] = value;
}
(Note that if the array is full old values will be overwritten, I am fine with that).
Does anyone sees a problem with this design? I suspect there might be a race condition I am not seeing.
A simpler approach is to use a power of 2 size and do the following.
final double[] array;
final int sizeMask;
final AtomicInteger i = new AtomicInteger();
public CircularBuffer(int size) {
assert size > 1 && ((size & (size -1)) == 0); // test power of 2.
array = new double[size];
sizeMask = size -1;
}
void add(double value) {
array[i.getAndIncrement() & sizeMask] = value;
}
Check out disruptor : http://lmax-exchange.github.io/disruptor/, it's an open-source lock-free circular buffer in Java.
Yes, there is a race condition.
Say i = array.length - 2, and two threads enter circularIncrementAndGet():
Thread 1: i.compareAndSet(array.length - 1, -1) results in i = array.length - 2
Thread 2: i.compareAndSet(array.length - 1, -1) results in i = array.length - 2
Thread 1: i.incrementAndGet() results in i = array.length - 1
Thread 2: i.incrementAndGet() results in i = array.length
leading to an ArrayIndexOutOfBoundsException when Thread 2 reaches array[idx] = value (and on all subsequent calls to add() until i overflows).
The solution proposed by #Peter Lawrey does not suffer from this problem.
If you stick with the following constraints:
Only one thread is allowed to modify the head pointer at any time
Only one thread is allowed to modify the tail pointer at any time
Dequeue-on-empty gives a return value indicating nothing was done
Enqueue-on-full gives a return value indicating nothing was done
You don't keep any count of how many values are stored in the queue.
You 'waste' one index in the array that will never be used, so that you can tell when the array is full or empty without having to keep count.
It is possible to implement a circular array/queue.
The enqueuing thread owns the tail pointer. The dequeueing thread owns the head pointer. Except for one condition, these two threads don't share any state so far, and so there are no problems.
That condition is testing for emptyness or fullness.
Consider empty to mean that head == tail; Consider full to mean tail == head - 1 modulo array size. Enqueue has to check to see if the queue is full, dequeue has to check to see if the queue is empty. You need to waste one index in the array to detect the difference between full and empty - if you enqueued into that last bucket, then full would be head == tail and empty would be head == tail and now you deadlock - you think you're empty and full at the same time, so no work would get done.
In performing these checks, its possible that one value could be updated while being compared. However since these two values are monotonically increasing, there is no correctness problem:
If, in the dequeue method, the head == tail computes to be true during the comparison, but tail moves forward just afterward, no problem - you thought the array was empty when it actually wasn't, but no big deal, you'll just return false from the dequeue method and try again.
If, in the enqueue method, the tail == head - 1 computes to be true, but just after so the head increments, then you'll think the array was full when it really wasn't, but again, no big deal, you'll just return false from enqueue and try again.
This is the design used behind the implementation I found in Dr. Dobb's years ago, and it has served me well:
http://www.drdobbs.com/parallel/lock-free-queues/208801974
Related
Suppose I have an int array, an element num and 4 threads.
I'm giving each thread 1/4 of the array to search for num.
(The search method is given below)
public static boolean contains(int[] array, int minIdx, int maxIdx, int num) { ...}
At my "top level", I can schedule 4 threads to search 4 quarters of the array, but how do I ensure ALL the threads stop searching as soon as one of them finds the element (assuming there is NO duplicate in the array, hence the element can appear at most once).
P.S: You see, suppose my 4th thread found the element at the first iteration, I want the top-level method to return immediately as opposed to wait for other 3 guys to complete.
You need explicit signaling. You might consider the built-in interruption mechanism, or you may roll your own because it's very simple.
One idea: share an AtomicBoolean among all the threads and let each one periodically check it. When a thread finds the answer, it flips the boolean. The best option to achieve periodic checking is a nested loop:
for (int i = start; i < end && !done.get();) {
for (int batchLimit = Math.min(i + BATCH_SIZE, end); i < batchLimit; i++) {
// your logic
}
}
This is the easiest for the JIT compiler to optimize.
The price of checking the value is very low whenever the value didn't change. It will be in the L3 cache. The case when the value did change is irrelevant because at that point you're done.
Use a flag to signal when you found the answer and share it between threads. AtomicBoolean is a good option.
Add the boolean to your loop end conditions for example
for (int i = minIdxs ; i < maxIdxs && found.get() == false; ++i){...}
Also share a CountDownLatch of size 4 and countDown() when you are returning from each thread.
Have your main thread await() and it'll mean all threads gracefully finish before you move on in your main thread.
You can write a class who will act like a controller. this class will know each thread and every thread knows the controller. (its like an observer pattern)
If one thread finds the answer, the thread can tell it to the controller which can inform the other threads to stop.
class ControllerOfAllTheThreads{
ArrayList<TheClassesWhichDoTheSearch> list = new ArrayList<TheClassesWhichDoTheSearch>();
public void tellThemWeFoundHim(){
for (TheClassesWhichDoTheSearch theThreads : list) {
if(theThreads.isAlive() && !theThreads.isInterrupted())
theThreads.interrupt();
}
}
}
public static void deleteLast(Vector list) {
int lastIndex = list.size() - 1;//line 2
list.remove(lastIndex); // line 3
}
I known Vector is threadsafe in java
but can this case happen
let's say in this case has list.size()=10
Thread A calls deleteLast and at line 2 lastIndex = 9 .It stops for some reason
Thread B call deleteLast and at line 2 lastIndex = 9.it goes to line 3 and now list has 9 elements
Thread A now wakes up and goes to line 3 now it tries to remove object at index 9 which doesn't exist and we have an exception here
Sure. You've correctly identified a race condition.
Yes, it can. And you should use a BlockingQueue rather than a List to avoid this scenario.
Is there a way to perform a "decrement if result is positive or zero" operation with an AtomicInteger?
To clarify the desired behavior:
if the current value is greater than zero, decrement
if the current value is equal to zero, do nothing
(negative current value is not handled)
In Java 8, yes:
atomicInteger.updateAndGet(i -> i > 0 ? i - 1 : i);
Before Java 8, no.
I suppose you could do something like this pre-Java 8:
int val = atomicInt.get();
boolean success = false;
while(val > 0 && !success) {
success = atomicInt.compareAndSet(val, val - 1);
if(!success) {
// Try again if the value is still > 0
val = atomicInt.get();
}
}
// Check 'success' to see if it worked
Not the most elegant code, but I think it does the trick.
Informal proof of correctness (by #Stephen C)
In the case where there is no other thread modifying the AtomicInteger, success will be set to true on the first compareAndSet call. So the code will be equivalent to
int val = atomicInt.get();
if (val > 0) {
atomicInt.compareAndSet(val, val - 1);
}
which is clearly correct.
In the case where some other thread modifies the AtomicInteger, between the get and the compareAndSet then the latter call will fail because the current value is no longer equal to val. So what happens then is that we call atomicInt.get() again to get the updated value ... and repeat. We keep repeating until either we succeeded in the compareAndSet OR the current val is less zero or less.
The net effect is that this thread EITHER decrements the AtomicInteger once, OR it gives up because it sees that the value is zero.
Note the following caveats:
The retry loop may result in another thread "overtaking" and getting its decrement in before our thread. (Another way of saying that is to say the algorithm is not "fair".)
If you immediately observed the value of the AtomicInteger after this sequence, you may observe that its value has changed ... again.
It is theoretically possible for the code to loop indefinitely. But that requires other threads to be continually updating the AtomicInteger.
However, none of these caveats is a violation of the (assumed) requirements.
Credit goes to #JB Nizet.
To know update is successful or not:
AtomicBoolean isUpdateSuccessful = new AtomicBoolean(false);
atomicInteger.updateAndGet( i -> {
if( i > 0 ) {
isUpdateSuccessful.getAndSet(true);
return i - 1;
} else {
isUpdateSuccessful.getAndSet(false);
return i;
}
});
I'm writing an algorithm which do a big loop over an integer array from the end to the beginning with a if condition inside. At the first time the condition is false the loop can be terminated.
So, with a for loop, if condition is false it continues to iterate with simple variables changes.
With a while loop with the condition as while parameter, the loop will stop once condition false and should save some iterations.
However, the while loop remains a little slower than the for loop!
But, if I put a int value as counter, and count iterations, the For loop as expected performed much more iterations.
However this time, the time execution of the mofified For method with the counter will be much more slower than the while method with a counter!
Any explanations?
here the code with a for loop:
for (int i = pairs.length - 1; i >= 0; i -= 2) {
//cpt++;
u = pairs[i];
v = pairs[i - 1];
duv = bfsResult.distanceMatrix.getDistance(u, v);
if (duv > delta) {
execute();
}
}
time execution: 6473
time execution with a counter: 8299
iterations counted: 2584401
here the code with the while loop:
int i = pairs.length - 1;
u = pairs[i];
v = pairs[i - 1];
duv = bfsResult.distanceMatrix.getDistance(u, v);
while (duv > delta) {
//cpt++;
execute();
u = pairs[i -= 2];
v = pairs[i - 1];
duv = bfsResult.distanceMatrix.getDistance(u, v);
}
time execution: 6632
time execution with a counter: 7163
iterations counted: 9793
Time is in ms, I repeated the experiment several times with different size intances, the measures remained almost the same. The execute() method updates the delta value. Method getDistance() is just a matrix int[][] access.
Thanks for any help.
Before you try to perform any performance tests on java I highly recommend you reading this article
http://www.ibm.com/developerworks/java/library/j-benchmark1/index.html
In a few words - when running for some time Hotspot-enabled JVM can optimize your code which will affect the results of tests. So you need proper technique to test performance of your code.
To ease the pain there is a library used for performing proper tests: http://ellipticgroup.com/html/benchmarkingArticle.html
You can find links to both parts of the article on this page.
Update: to help you start quicker with this here is what you just need to do:
Download bb.jar, jsci-core.jar, mt-13.jar found on the page
Put them on classpath
Rewrite your code so that while loop approach and for loop approach both go in separate implementations of Runnable or Callable interface
In your main method just invoke
System.out.println(new Benchmark(new WhileApproach()));
to show execution time for while-loop and obviously
System.out.println(new Benchmark(new ForApproach()));
to get info for for-loop
You do not have the same termination condition. For the while loop it's:
duv > delta
and for the for loop it's
i >= 0
The two scenarios are not equivalent. My guess is that the while loop condition becomes false way sooner than the for condition and therefore it executes less iterations.
When duv>delta the while-loop stops, but the for-loop continues. Both get the same result, but for continues checking. You should modify the for-loop like this:
if (duv > delta)
{
execute();
}
else break;
Will the following code cause race condition issue if several threads invoke the "incrementCount" method?
public class sample {
private AtomicInteger counter = new AtomicInteger(0);
public int getCurrentCount {
int current = counter.getAndIncrement();
if (counter.compareAndSet(8, 0)) current = 0;
return current;
}
}
If it causes race condition, what are the possible solution other than using synchronized keyword?
You probably don't want to let the counter exceed 8 and this won't work. There are race conditions.
It looks like you want a mod 8 counter. The easiest way is to leave the AtomicInteger alone and use something like
int current = counter.getAndIncrement() & 7;
(which is fixed and optimized version of % 8). For computations mod 8 or any other power of two it works perfectly, for other number you'd need % N and get problems with int overflowing to negative numbers.
The direct solution goes as follows
public int getCurrentCount {
while (true) {
int current = counter.get();
int next = (current+1) % 8;
if (counter.compareAndSet(current, next))) return next;
}
}
This is about how getAndIncrement() itself works, just slightly modified.
Yes, it probably does not do what you want (there is a kind of race condition).
One thread may call getAndIncrement() and receive a 8
A second thread may call getAndIncrement() and receive a 9
The first thread tries compareAndSet but the value is not 8
The second thread tries compareAndSet but the value is not 8
If there's no risk of overflowing, you could do something like
return counter.getAndIncrement() % 8;
Relying on that something does not overflow seems like a poor idea to me though, and I would probably do roughly what you do, but let the method be synchronized.
Related question: Modular increment with Java's Atomic classes
What are you trying to achieve? Even if you use the fixes proposed by ajoobe or maartinus you can end up with different threads getting the same answer - consider 20 threads running simultaneously. I don't see any interesting significance of this "counter" as you present it here - you may as well just pick a random number between 0 and 8.
Based on the code for getAndIncrement()
public int getCurrentCount() {
for(;;) {
int courrent = counter.get();
int next = current + 1;
if (next >= 8) next = 0;
if (counter.compareAndSet(current, next))
return current;
}
}
However a simpler implementation in your case is to do
public int getCurrentCount() {
return counter.getAndIncrement() & 0x7;
}
I assume that the what you want is to have a counter form 0 to 7.
If that is the case then a race condition can possibly happen and the value of counter can become 9.
Unless you are ok to use % soln. as said by others, you micht have to use synchronized.