0

I would like to be able to run two methods at the same time that rely on the same global variable. The first method periodically updates the shared variable, but never finishes running. The second method keeps track of time. When time runs out, the second method returns the last result of the shared variable from the first method. Below is what I have so far, with commented out pseduocode in the places where I need help.

package learning;

public class testmath{
    public static void main(String[] args){

        long finishBy = 10000;
        int currentresult = 0;

        /*
         * run eversquare(0) in  a seperate thread /in parallel
         */


        int finalresult = manager(finishBy);
        System.out.println(finalresult);
    }


    public static int square(int x){
        return x * x;
    }

    public static void eversquare(int x){
        int newresult;
        while(2 == 2){
            x += 1;
            newresult = square(x);
            /*
             * Store newresult as a global called currentresult
             */

        }
    }



    public static int manager(long finishBy){


        while(System.currentTimeMillis() + 1000 < finishBy){
            Thread.sleep(100);
        }

        /*
         * Access global called currentresult and create a local called currentresult
         */     
        return currentresult;

    }

}
4
  • 1
    Why while(2 == 2) rather than while(true)? Remember you can synchronize a block within a method, not just a whole method. Commented Jun 17, 2014 at 19:38
  • 2
    Read the concurrency tutorial, and try something: docs.oracle.com/javase/tutorial/essential/concurrency/… Commented Jun 17, 2014 at 19:41
  • @PatriciaShanahan they have equivalent boolean values, will use true in the future. Started learning java three days ago, could you explain a little more what you mean by "you can synchronize a block within a method, not just a whole method" and how it would help me here. Commented Jun 17, 2014 at 19:48
  • 1
    You may wish to consider using an AtomicInteger for the shared value. journaldev.com/1095/… Commented Jun 17, 2014 at 20:02

1 Answer 1

2

You only need to run one additional thread:

public class Main {
  /**
   * Delay in milliseconds until finished.
   */
  private static final long FINISH_BY = 10000;
  /**
   * Start with this number.
   */
  private static final int START_WITH = 1;
  /**
   * Delay between eversquare passes in milliseconds.
   */
  private static final long DELAY_BETWEEN_PASSES = 50;
  /**
   * Holds the current result. The "volatile" keyword tells the JVM that the
   * value could be changed by another thread, so don't cache it. Marking a
   * variable as volatile incurs a *serious* performance hit so don't use it
   * unless really necessary.
   */
  private static volatile int currentResult = 0;

  public static void main(String[] args) {
    // create a Thread to run "eversquare" in parallel
    Thread eversquareThread = new Thread(new Runnable() {
      @Override public void run() {
        eversquare(START_WITH, DELAY_BETWEEN_PASSES);
      }
    });

    // make the eversquare thread shut down when the "main" method exits
    // (otherwise the program would never finish, since the "eversquare" thread
    // would run forever due to its "while" loop)
    eversquareThread.setDaemon(true);

    // start the eversquare thread
    eversquareThread.start();

    // wait until the specified delay is up
    long currentTime = System.currentTimeMillis();
    final long stopTime = currentTime + FINISH_BY;
    while (currentTime < stopTime) {
      final long sleepTime = stopTime - currentTime;
      try {
        Thread.sleep(sleepTime);
      } catch (InterruptedException ex) {
        // in the unlikely event of an InterruptedException, do nothing since
        // the "while" loop will continue until done anyway
      }
      currentTime = System.currentTimeMillis();
    }
    System.out.println(currentResult);
  }

  /**
   * Increment the value and compute its square. Runs forever if left to its own
   * devices.
   *
   * @param startValue
   * The value to start with.
   *
   * @param delay
   * If you were to try to run this without any delay between passes, it would
   * max out the CPU and starve any other threads. This value is the wait time
   * between passes.
   */
  private static void eversquare(final int startValue, final long delay) {
    int currentValue = startValue;
    while (true) { // run forever (just use "true"; "2==2" looks silly)
      currentResult = square(currentValue); // store in the global "currentResult"
      currentValue++; // even shorter than "x += 1"
      if (delay > 0) {
        try { // need to handle the exception that "Thread.sleep()" can throw
          Thread.sleep(delay);
        } catch (InterruptedException ex) { // "Thread.sleep()" can throw this
          // just print to the console in the unlikely event of an
          // InterruptedException--things will continue fine
          ex.printStackTrace();
        }
      }
    }
  }

  private static int square(int x) {
    return x * x;
  }
}

I should also mention that the "volatile" keyword works for (most) primitives, since any JVM you'll see these days guarantees they will be modified atomically. This is not the case for objects, and you will need to use synchronized blocks and locks to ensure they are always "seen" in a consistent state.

Most people will also mention that you really should not use the synchronized keyword on the method itself, and instead synchronize on a specific "lock" object. And generally this lock object should not be visible outside your code. This helps prevent people from using your code incorrectly, getting themselves into trouble, and then trying to blame you. :)

Sign up to request clarification or add additional context in comments.

8 Comments

This is extremely helpful. The only thing that is unclear to me is why the delay is necessary. My understanding is that adding multiple threads allows the program to make use of a computer's multiple CPUs. As I read your code it wastes valuable computation time by turning off the function for 50 milliseconds every time it returns a value. I guess it depends what a program is for and the different types of machines it will run on.
This is obviously a toy example, with DELAY_BETWEEN_PASSES = 0 the function gets up to 1516175360, wheras a delay of 50 milliseconds, it only gets to 40401.
@Michael: Since Java (in theory) can run anywhere, you can't make any assumptions about the underlying OS and CPU. This is especially true since more and more people are deploying to "the cloud". If it happened to run where only one core is available, the eversquare thread could suck up so much CPU time, the main method/thread may never get enough CPU time to wake up, or it might walk up late. Even on machines that have multiple cores, AFAIK the JVM isn't obligated to use them for it's threads. Someone correct me if I'm wrong about that.
Good code. However, you should never consume InterruptedException. Just because you are a main does not mean you are not being called by another thread. ALWAYS re-throw!
In this case if you have multiple cores on your computer, it likely will run fine. But it's not a guarantee everywhere and it's generally much better to design code that avoids doing that. Since you're effectively benchmarking the CPU, you pretty much have to set it to zero. But I included it to give an example of good (well, better) design and to alert you that's something you need to consider.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.