Вообщем читаю книгу и в ней есть небольшая глава про многопоточность, так вот я столкнулся с одним примером который не совсем могу понять, вот его код:
// Try This 12-1

// A simulation of a traffic light that uses
// an enumeration to describe the light's color.

// An enumeration of the colors of a traffic light.
enum TrafficLightColor {
  RED, GREEN, YELLOW
}

// A computerized traffic light.
class TrafficLightSimulator implements Runnable {
  private TrafficLightColor tlc; // holds the current traffic light color
  private boolean stop = false; // set to true to stop the simulation
  private boolean changed = false; // true when the light has changed

  TrafficLightSimulator(TrafficLightColor init) {
    tlc = init;
  }

  TrafficLightSimulator() {
    tlc = TrafficLightColor.RED;
  }

  // Start up the light.
  public void run() {
    while(!stop) {
      try {
        switch(tlc) {
          case GREEN:
            Thread.sleep(10000); // green for 10 seconds
            break;
          case YELLOW:
            Thread.sleep(2000);  // yellow for 2 seconds
            break;
          case RED:
            Thread.sleep(12000); // red for 12 seconds
            break;
        }
      } catch(InterruptedException exc) {
        System.out.println(exc);
      }
      changeColor();
    }
  }

  // Change color.
  synchronized void changeColor() {
    switch(tlc) {
      case RED:
        tlc = TrafficLightColor.GREEN;
        break;
      case YELLOW:
        tlc = TrafficLightColor.RED;
        break;
      case GREEN:
       tlc = TrafficLightColor.YELLOW;
    }

    changed = true;
    notify(); // signal that the light has changed
  }

  // Wait until a light change occurs.
  synchronized void waitForChange() {
    try {
      while(!changed)
        wait(); // wait for light to change
      changed = false;
    } catch(InterruptedException exc) {
      System.out.println(exc);
    }
  }

  // Return current color.
  synchronized TrafficLightColor getColor() {
    return tlc;
  }

  // Stop the traffic light.
  synchronized void cancel() {
    stop = true;
  }
}

class TrafficLightDemo {
  public static void main(String[] args) {
    TrafficLightSimulator tl =
      new TrafficLightSimulator(TrafficLightColor.GREEN);

    Thread thrd = new Thread(tl);
    thrd.start();

    for(int i=0; i < 9; i++) {
      System.out.println(tl.getColor());
      tl.waitForChange();
    }

    tl.cancel();
  }
}
Так вот в чем суть вопроса, в методе synchronized void waitForChange() есть цикл while который на вход принимает переменную !changed по умолчанию она false там она с восклицательным знаком , а значит что не false то true и цикл запустится и приостановит выполнение потока main(так я себе это представляю) идем дальше в другом потоке под названием thrd есть метод synchronized void changeColor() который в самом конце этой же переменной changed присваивает значение true и вызывает метод notify() который должен запустить поток main, который заново запустит метод synchronized void waitForChange() но я не могу понять как в нем запустится цикл while который снова приостановит поток main если переменной changed присвоили true , а tru с восклицательным знаком это false? Уже пару ночей не могу понять как этот код работает