I've been working on implementing a custom Cyclic Barrier which adds values passed into the await method and returns the sum to all threads when after notify is called.
The code:
public class Barrier {
private final int parties;
private int partiesArrived = 0;
private volatile int sum = 0;
private volatile int oldSum = 0;
public Barrier(int parties) {
if (parties < 1) throw new IllegalArgumentException("Number of parties has to be 1 or higher.");
this.parties = parties;
}
public int getParties() { return parties; }
public synchronized int waitBarrier(int value) throws InterruptedException {
partiesArrived = 1;
sum = value;
if (partiesArrived != parties) {
wait();
}
else {
oldSum = sum;
sum = 0;
partiesArrived = 0;
notifyAll();
}
return oldSum;
}
public int getNumberWaiting() { return partiesArrived; }
}
This works, but I hear that there is a way to change the values sum
and oldSum
(or at least oldSum
) into local variables of the waitBarrier
method. However, after racking my head over it, I don't see a way.
Is it possible and , if yes, how?
CodePudding user response:
However, after racking my head over it, I don't see a way.
Quite so.
Is it possible and , if yes, how?
it is not possible.
For some proof:
Try marking a local var as volatile
. It won't work: The compiler doesn't allow it. Why doesn't it? Because volatile is neccessarily a no-op: local vars simply cannot be shared with another thread.
One might think this is 'sharing' a local:
void test() {
int aLocalVar = 10;
Thread t = new Thread(() -> {
System.out.println("Wow, we're sharing it! " aLocalVar);
});
t.start();
}
But it's some syntax sugar tripping you up there: Actually (and you can confirm this with javap -c -v
to show the bytecode that javac
makes for this code), a copy of the local var is handed to the block here. This then explains why, in java, the above fails to compile unless the variable you're trying to share is either [A] marked final
or [B] could have been so marked without error (this is called 'the variable is effectively final'). Had java allowed you to access non-(effectively) finals like this, and had java used the copy mechanism that is available, that would be incredibly confusing.
Of course, in java, all non-primitives are references. Pointers, in the parlance of some other languages. Thus, you can 'share' (not really, it'll be a copy) a local var and nevertheless get what you want (share state between 2 threads), because whilst you get a copy of the variable, the variable is just a pointer. It's like this: If I have a piece of paper and it is mine, but I can toss it in a photocopier and give you a copy too, we can't, seemingly, share state. Whatever I scratch on my paper won't magically appear on yours; it's not voodoo paper. But, if there is an address to a house on my paper and I copy it and hand you a copy, it feels like we're sharing that: If you walk over to the house and, I dunno, toss a brick through a window, and I walk over later, I can see it.
Many objects in java are immutable (impervious to bricks), and the primitives aren't references. One solution is to use the AtomicX
family which are just simplistic wrappers around a primitive or reference, making them mutable:
AtomicInteger v = new AtomicInteger();
Thread t = new Thread(() -> {v.set(10);});
t.start();
t.yield();
System.out.println(t.get());
// prints 10
But no actual sharing of a local happened here. The thread got a -copy- of the reference to a single AtomicInteger instance that lives on the heap, and both threads ended up 'walking over to the house', here.
CodePudding user response:
You can return sum
and have the first party clear it:
public synchronized int waitBarrier(int value) throws InterruptedException {
if (partiesArrived == 0) {
sum = 0;
}
partiesArrived ;
sum = value;
if (partiesArrived == parties) {
notifyAll();
} else {
while (partiesArrived < parties) {
wait();
}
}
return sum;
}
Note that the wait condition should always be checked in a loop in case of spurious wakeups. Also, sum
doesn't need to be volatile
if it's not accessed outside the synchronized
block.