0

So my task is this:

  1. Instantiate two object of the same class

  2. Provide a constructor argument, to designate a thread as even and another as odd .

  3. Start both threads right one after other

  4. Odd thread prints odd numbers from 0 to 1000

  5. Even thread prints even numbers from 0 to 1000

  6. However they should be in sync the prints should be 1 , 2 , 3 , 4 .....

  7. One number on each line

However I can't seem to get the locks to release correctly. I've tried reading some of the similar problems on here but they all use multiple classes. What am I doing wrong?

Edit: My main class is doing this -

NumberPrinter oddPrinter = new NumberPrinter("odd");
NumberPrinter evenPrinter = new NumberPrinter("even");

oddPrinter.start();
evenPrinter.start();

and my output is - odd: 1 even: 2 ...

public class NumberPrinter extends Thread {

private String name;
private int starterInt;
private boolean toggle;


public NumberPrinter(String name) {
    super.setName(name);
    this.name=name;

    if(name.equals("odd")) {
        starterInt=1;
        toggle = true;

    }
    else if(name.equals("even")) {
        starterInt=2;
        toggle = false;
    }
}

@Override
public synchronized void run() {

    int localInt = starterInt;
    boolean localToggle = toggle;

    if(name.equals("odd")) {


    while(localInt<1000) {

        while(localToggle == false)
        try {
            wait();
        }catch(InterruptedException e) {

              System.out.println("Main thread Interrupted");

        }
    System.out.println(name+": "+localInt);
    localInt +=2;
    localToggle = false;
    notify();

       }
    }

    else {

        while(localInt<1000) {


            while(localToggle == true)
                try {
                    wait();
                }catch(InterruptedException e) {

                      System.out.println("Main thread Interrupted");

                }
            System.out.println(name+": "+localInt);
            localInt +=2;
            localToggle = true;
            notify();
    }
}
}
}
Scary Wombat
  • 44,617
  • 6
  • 35
  • 64
  • 1
    Make `boolean localToggle` a field of type `static AtomicBoolean` At the moment they are method variables and only visible to the current Thread. – Scary Wombat Aug 22 '19 at 00:37
  • see this answer https://stackoverflow.com/a/12045001/2310289 – Scary Wombat Aug 22 '19 at 00:41
  • You are using wrong the `synchronized`, it only guarantee you concurrence when you are having more than one call to the method of the same object, look at this [documentation](https://docs.oracle.com/javase/tutorial/essential/concurrency/syncmeth.html) – Torgon Aug 22 '19 at 00:43
  • @Torgon Am I understanding correctly that if I were to put the part of the code that prints into a separate method, then change my run() method to call that method repeatedly with a while loop it would work as intended? – Christopher Belica Aug 22 '19 at 00:59
  • Change `boolean localToggle` -> `volatile boolean localToggle`, otherwise changes are not guaranteed to be visible (and frequently are not visible) outside the thread that made the change. – Bohemian Aug 22 '19 at 01:06
  • @ChristopherBelica Not exactly, thinking about this problem a little what you have is two differents threads trying to share the same resource, printing something to terminal and the way you resolve this particular problem is with Productor-Consumer model. In this model one thread waits untils the other finish to use some share resource, i'll leave you with this [definition](https://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem) and this [post](https://www.geeksforgeeks.org/producer-consumer-problem-using-semaphores-set-1/) about the problem. – Torgon Aug 22 '19 at 01:08
  • Also, prefer implementing everything as a `Runnable` and passing to the `Thread` constructor over extending `Thread`. – Bohemian Aug 22 '19 at 01:08
  • The only thing for you to complete the task is to adapt the model to your specific problem and it's done – Torgon Aug 22 '19 at 01:10
  • Same homework problem, different year: https://stackoverflow.com/questions/15182418/java-printing-odd-even-numbers-using-2-threads – Bohemian Aug 22 '19 at 01:10
  • 1
    @Bohemian As the OP is instantiating two Objects, then doesn't the `volatile boolean localToggle` need to be a static field in order for its state to be shared across different threads? – Scary Wombat Aug 22 '19 at 01:26
  • 1
    If you want to respect encountering order as stated above 1 , 2 , 3 , 4 ....., then this is not a good candidate for parallelizing. Please use the sequential approach instead. – Ravindra Ranwala Aug 22 '19 at 02:37
  • @ScaryWombat ah, yeah. Being `static` would help too. – Bohemian Aug 22 '19 at 10:46
  • I closed this as a duplicate, but then reconsidered, since it looks like the bug in this case is a little different. This code has no shared state between the threads, while the other question correctly shares an object for coordination but has some bugs in how it's used. – erickson Aug 22 '19 at 16:46

2 Answers2

0

The key problem here is that the two threads have no way to coordinate with each other. When you have a local variable (localToggle in this case) nothing outside the method can observe or alter its value.

If you share one object with both threads, however, its state can change, and if used correctly, those state changes will be visible to both threads.

You will see examples where the shared object is an AtomicInteger, but when you use synchronized, wait() and notify(), you don't need the extra concurrency overhead built into the atomic wrappers.

Here's a simple outline:

class Main {
  public static main(String... args) {
    Main state = new Main();
    new Thread(new Counter(state, false)).start();
    new Thread(new Counter(state, true)).start();
  }
  int counter;
  private static class Counter implements Runnable {
    private final Main state;
    private final boolean even;
    Counter(Main state, boolean even) {
      this.state = state;
      this.even = even;
    }
    @Override
    public void run() {
      synchronized(state) {
        /* Here, use wait and notify to read and update state.counter 
         * appropriately according to the "even" flag.
         */
      }
    }
  }
}

I'm not clear whether using wait() and notify() yourself is part of the assignment, but an alternative to this outline would be to use something like a BlockingQueue to pass a token back and forth between the two threads. The (error-prone) condition monitoring would be built into the queue, cleaning up your code and making mistakes less likely.

erickson
  • 265,237
  • 58
  • 395
  • 493
0

I finally got it working in a way that meets the standards required by my assignment. Thank you all for your input. I'll leave the answer here for anyone who might need it.

public class Demo {

public static void main(String[] args) {

    NumberPrinter oddPrinter = new NumberPrinter("odd");
    NumberPrinter evenPrinter = new NumberPrinter("even");



    oddPrinter.start();
    evenPrinter.start();




    System.out.println("Calling thread Done");

}

public class NumberPrinter extends Thread {

private int max = 1000;
static Object lock = new Object();
String name;
int remainder;
static int startNumber=1;



public NumberPrinter(String name) {

        this.name = name;

        if(name.equals("even")) {
            remainder=0;

        }else {
            remainder=1;


        }
    }

@Override
public void run() {



        while(startNumber<max) {
            synchronized(lock) {
                while(startNumber%2 !=remainder) {
                    try {
                    lock.wait();
                    }catch(InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(name+": "+startNumber);
                startNumber++;
                lock.notifyAll();
            }
        }

}

}