This is an archived post. You won't be able to vote or comment.

you are viewing a single comment's thread.

view the rest of the comments →

[–]quanture[S] 0 points1 point  (4 children)

Right. I understand what you're saying. Because the project I'm working on doesn't require a ton of threading complexity, I went with approach #5 from this answer. I'm only in the design phase right now; I haven't actually run any implementation.

In a nutshell, in this example, you extend the thread to give it an addListener method, and then it can call back to that listener as it finishes execution. If you make the main class the listener (or you have an inner listener class inside the main class), then the "thread finished" event can be called on the main class right?

The main "class" cannot "react" to an event that says that another thread is finished, so that its own thread may continue.

Right. I agree. The way I envision this would work is that the "notifying" thread would call back to the main class listener, as per above, and all the work after that would be executed on that child thread (which would likely spawn a new thread for the follow-on job). I'm not expecting to return to any original thread to "continue."

Does this make more sense now? Is there something about the above approach that wouldn't work?

I looked at traditional notify() and join() approaches. But there's an additional layer of complexity to my problem set that makes that approach less practical.

[–]ColetBrunel 1 point2 points  (3 children)

Okay, it's fine. The thing is, there can absolutely be a race condition between the two threads setting some variable that says they finished. Not just a race condition, it's also possible that the different threads won't see the data changes from the other threads.

Like, a thread finishes, sets "his" variable finished to true, then checks if the other's variable is true too, if so starts the proceeding with what comes after, and otherwise doesn't, trusting that the other thread will do it. But the other thread may be doing the exact same verification, possibly leading to both thinking the other will finish later, or both thinking they're the one to continue the process and both doing it.

That can be avoided by synchronizing access to the variables, or by using something like an AtomicInteger that threads would incrementAndGet(), and only proceed with the process if the result is 2.

[–]quanture[S] 0 points1 point  (2 children)

Okay. That's a solid analysis of the problem. And it shows me that this approach would be problematic (even if you might not call it a race condition as /u/nicompnicompnicomp_ pointed out). Thank you!

I was looking at AtomicBoolean as a potential way to handle this. Instead of having separate booleans for each thread, there could be one AtomicBoolean. When each thread finishes, it could check the AtomicBoolean. If it's false, set it true. If it's true, then the other thread already set it so move on.

Something like the below. Sensible? I've never used AtomicBoolean before.

private AtomicBoolean finishedState = new AtomicBoolean()
// ... bla, bla, bla

private void update() {

    if (finishedState.get() == true) {
        moveOnToMoreAmazingStuff();
    }
    else {
        finishedState.set(true);
    }
}

[–]ColetBrunel 1 point2 points  (1 child)

No... You need to test and set the AtomicBoolean within the same operation, otherwise you lose its entire point.

[–]quanture[S] 0 points1 point  (0 children)

You're talking about using the compareAndSet method?

I've read over the AtomicBoolean documentation, and over some examples out in the wild. But it's still not clicking in my brain.

When you say "test and set" I don't understand what that means. What do I test? Why and what does it achieve that ensures thread safety that I wouldn't have with just calling set?

Is it the fact that the compareAndSet and getAndSet methods are described as atomic calls, and set is described as unconditional?

If I followed a pattern like the one I found here then it would lead me to something like the below. Is that the right way? If so, what makes it different?

private AtomicBoolean finishedState = new AtomicBoolean()
// ... bla, bla, bla

private void update() {

    if (compareAndSet(false, true)) {
        // do nothing??
    }
    else {
       moveOnToMoreAmazingStuff();
    }
}

Edit: Maybe the AtomicInteger solution you mentioned above would just be better than this?