[concurrency-interest] Java ForkJoin demo applications

Dr Heinz M. Kabutz heinz at javaspecialists.eu
Thu Dec 8 17:30:58 EST 2011


Thanks Doug.  I had read that comment, but did not realize the 
implications.  Like most of the 10_000_000 Java programmers out there, 
I'm still new to Fork/Join.  For example, I found it interesting to 
discover that all the F/J threads are daemons.  Also that you cannot 
interrupt the threads using the standard cancel() mechanism.  This makes 
sense, of course, as F/J is meant to be used to do a calculation in 
parallel, not to call blocking methods.

I wrote a little class that demonstrates the visibility of state 
nicely.  If you run it, the compute() method forks two tasks.  f2 is 
reading running, which happens to be its own field.  As a result, the 
value is typically inlined and so changes are not visible anymore.  Task 
f1 sleeps for a second and then sets the field to "false".  The main 
task does a join on f1, so that thread sees the value change, as you 
explained.  The other thread, however, keeps on running forever.

The daemon threads make this whole exercise a bit deceptive.  Let's say 
we do not join() the thread that was forked.  That would probably be a 
coding mistake.  But if we did that, then the task would occupy one core 
at 100% and there would be no way to cancel it.

import java.util.concurrent.*;

/**
 * @author Heinz Kabutz
 */
public class ForkVisibility extends RecursiveAction {
  private static class ChangeRunningFlag extends RecursiveAction {
    private final WatchRunningFlag wrf;

    private ChangeRunningFlag(WatchRunningFlag wrf) {
      this.wrf = wrf;
    }

    protected void compute() {
      try {
        Thread.sleep(1000);
      } catch (InterruptedException e) {
        // not sure what to do with the interrupted exception here
        Thread.currentThread().interrupt();
      }
      wrf.running = false;
    }
  }

  private static class WatchRunningFlag extends RecursiveAction {
    private boolean running = true;

    protected void compute() {
      while (running) ;
      System.out.println("We have stopped running");
    }
  }

  protected void compute() {
    WatchRunningFlag f2 = new WatchRunningFlag();
    f2.fork();

    ChangeRunningFlag f1 = new ChangeRunningFlag(f2);
    f1.fork();

    f1.join();
    System.out.println("running = " + f2.running);

    System.out.println("Now let's wait for WatchRunningFlag");
    f2.join();
    System.out.println("f2.isDone() = " + f2.isDone());
  }

  public static void main(String[] args) throws InterruptedException {
    ForkJoinPool pool = new ForkJoinPool();
    ForkVisibility f = new ForkVisibility();
    pool.invoke(f);
    System.out.println(pool);
  }
}

Regards

Heinz
-- 
Dr Heinz M. Kabutz (PhD CompSci)
Author of "The Java(tm) Specialists' Newsletter"
Sun Java Champion
IEEE Certified Software Development Professional
http://www.javaspecialists.eu
Tel: +30 69 72 850 460
Skype: kabutz 



On 12/8/11 11:09 PM, Doug Lea wrote:
> On 12/08/11 16:01, Dr Heinz M. Kabutz wrote:
>> Hi Doug,
>>
>> in your Fib example:
>> http://gee.cs.oswego.edu/cgi-bin/viewcvs.cgi/jsr166/src/test/loops/Fib.java?revision=1.5&view=markup 
>>
>>
>>
>> should "number" not be marked as volatile, as it is accessed from 
>> multiple threads?
>>
>
> No. See The javadocs for some explanation. Pasting from the spec for 
> ForkJoinTask.fork:
>
> Subsequent modifications to the state of this task or any data it 
> operates on are not necessarily consistently observable by any thread 
> other than the one executing it unless preceded by a call to join()  
> or related methods, or a call to isDone()  returning true.
>
> As far as memory effects go, reading the results of a joined
> computation are basically the same as reading data after
> reading a volatile "ready" flag. Usually (in both cases)
> you do not need to declare the data itself volatile.
>
> -Doug
>
>


More information about the Concurrency-interest mailing list