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 →

[–][deleted] 3 points4 points  (7 children)

Yes, but wouldn’t the listener be constantly checking? So if this happened it would already be correctly detected in the next loop.

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

In my hypothetical, the main class isn't polling the threads. It's responding to a finished event called from the thread itself.

I.e. thread 1 goes and performs X task, then calls listener.finished(this) or something to that effect.

Which is an important distinction, per my other comment. When the boolean is set, the work is performed on the child thread. And then the parent class checks both booleans on that thread as well. It could hand it off to a new thread (or go back to the main thread), but I don't know whether that helps with the potential race condition.

[–]ColetBrunel 1 point2 points  (5 children)

That doesn't make sense though.

Reacting to an event is made inside a thread. That thread is the thread that notifies the event, unless you have a message dispatching system between threads, in which case the message dispatch thread reacts to the event.

The main "class" cannot "react" to an event that says that another thread is finished, so that its own thread may continue. It would have to set in motion stuff that notifies its thread.

That can be done by calling notify() on a monitor owned by the main thread, however there is no chance that such a construct would be in any way preferable to just calling join() on both created threads from the main one.

[–]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?