0

I have a small program presenting the user with a GUI to select a file, the program then executes based on the files contents and presents the results accordingly.

This is the loop running in the main thread (the main() method):

do {
    args = fg.getFile();
} while (!fg.started);

fg.started is a boolean variable set to true when the user has selected a file and presses the "start" button.

However, this does not work UNLESS I put a random task inside the loop:

do {
    args = fg.getFile();
    System.out.println("");
} while (!fg.started);

This works.

Can anyone explain to me why this is?

I'm aware of a solution involving the Thread class, with notify() and wait() etc, but now I'm just curious about this.

sakura
  • 2,249
  • 2
  • 26
  • 39
  • possible duplicate of [Loop doesn't see changed value without a print statement](http://stackoverflow.com/questions/25425130/loop-doesnt-see-changed-value-without-a-print-statement) – Boann Aug 23 '14 at 10:22

2 Answers2

2

You should syncronize the access of fg otherwise this may happen:

  • Thread1 could possibly become active, access and therefor block fg, and then get paused.
  • Thread2 becomes active, can't access fg and get's deactivated immediately.
  • Thread1 is active again, finishes the task an may instantaniously reaccess fg, then pause.
  • Thread2 becomse active and ... damn! fg is locked again!

If you need help/instructions on concurrency/parallel execution, see Oracle - Syncronization for information.

ifloop
  • 8,079
  • 2
  • 26
  • 35
2

fg.started is a boolean variable set to true when the user has selected a file and presses the "start" button.

However, this does not work UNLESS I put a random task inside the loop:

That's because two threads are accessing the started field without memory synchronization. You don't show the definition of fg.started but I suspect that you need to make sure that it is volatile. You also might consider switching to an AtomicBoolean. Here's the Java tutorial about memory synchronization.

volatile boolean started;

The issue with multi-threaded applications is that parts of your program can run in different processors with their own local memory cache. Whenever threads modify or access shared fields there needs to be explicit synchronization to ensure that they aren't just looking at the local cached value of the field.

It is working when you add a System.out.println(...) because the PrintStream's methods are synchronized so you are adding indirect memory synchronization.

making getFile() synchronized fixed it, thanks. Care to explain why? :)

Because, like the System.out.println(...) you are adding indirect synchronization.

Community
  • 1
  • 1
Gray
  • 115,027
  • 24
  • 293
  • 354
  • Ahh, great. Now I understand why the sysouting works :) – Hans Petter Taugbøl Kragset Apr 10 '14 at 13:52
  • you should relax, you seem like you think points are the most important thing in the world. If you want to help the community by making the best answers have the most points, try not to act like you want the most points for yourself – Hans Petter Taugbøl Kragset Apr 10 '14 at 14:01
  • I'm very relaxed. :-) It's not about points @hewo. It's about correct answers for posterity. It's about rewarding experience. It's about getting invalid answers corrected. – Gray Apr 10 '14 at 14:04
  • "...adding indirect synchronization." The Java Language Spec promises that if thread A updates a field and then releases some lock, and then thread B later acquires the same lock, then thread B should be able to see the updated field value. One easy way to implement that is, releasing a lock flushes the local cache, and acquiring a lock invalidates the local cache. Depending on the architecture and the JVM implementation, it might be sufficient for just thread A to acquire and release a lock in order to make the update (eventually) become visible to thread B. – Solomon Slow Apr 10 '14 at 15:15