//: c13:ThreadVariations.java
// Creating threads with inner classes.
// From 'Thinking in Java, 3rd ed.' (c) Bruce Eckel 2002
// www.BruceEckel.com. See copyright notice in CopyRight.txt.
import com.bruceeckel.simpletest.*;

// Using a named inner class:
class InnerThread1 {
  private int countDown = 5;
  private Inner inner;
  private class Inner extends Thread {
    Inner(String name) {
      super(name);
      start();
    }
    public void run() {
      while(true) {
        System.out.println(this);
        if(--countDown == 0) return;
        try {
          sleep(10);
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }
      }
    }
    public String toString() {
      return getName() + ": " + countDown;
    }
  }
  public InnerThread1(String name) {
    inner = new Inner(name);
  }
}

// Using an anonymous inner class:
class InnerThread2 {
  private int countDown = 5;
  private Thread t;
  public InnerThread2(String name) {
    t = new Thread(name) {
      public void run() {
        while(true) {
          System.out.println(this);
          if(--countDown == 0) return;
          try {
            sleep(10);
          } catch (InterruptedException e) {
            throw new RuntimeException(e);
          }
        }
      }
      public String toString() {
        return getName() + ": " + countDown;
      }
    };
    t.start();
  }
}

// Using a named Runnable implementation:
class InnerRunnable1 {
  private int countDown = 5;
  private Inner inner;
  private class Inner implements Runnable {
    Thread t;
    Inner(String name) {
      t = new Thread(this, name);
      t.start();
    }
    public void run() {
      while(true) {
        System.out.println(this);
        if(--countDown == 0) return;
        try {
          Thread.sleep(10);
        } catch (InterruptedException e) {
          throw new RuntimeException(e);
        }
      }
    }
    public String toString() {
      return t.getName() + ": " + countDown;
    }
  }
  public InnerRunnable1(String name) {
    inner = new Inner(name);
  }
}

// Using an anonymous Runnable implementation:
class InnerRunnable2 {
  private int countDown = 5;
  private Thread t;
  public InnerRunnable2(String name) {
    t = new Thread(new Runnable() {
      public void run() {
        while(true) {
          System.out.println(this);
          if(--countDown == 0) return;
          try {
            Thread.sleep(10);
          } catch (InterruptedException e) {
            throw new RuntimeException(e);
          }
        }
      }
      public String toString() {
        return Thread.currentThread().getName() +
          ": " + countDown;
      }
    }, name);
    t.start();
  }
}

// A separate method to run some code as a thread:
class ThreadMethod {
  private int countDown = 5;
  private Thread t;
  private String name;
  public ThreadMethod(String name) { this.name = name; }
  public void runThread() {
    if(t == null) {
      t = new Thread(name) {
        public void run() {
          while(true) {
            System.out.println(this);
            if(--countDown == 0) return;
            try {
              sleep(10);
            } catch (InterruptedException e) {
              throw new RuntimeException(e);
            }
          }
        }
        public String toString() {
          return getName() + ": " + countDown;
        }
      };
      t.start();
    }
  }
}

public class ThreadVariations {
  private static Test monitor = new Test();
  public static void main(String[] args) {
    new InnerThread1("InnerThread1");
    new InnerThread2("InnerThread2");
    new InnerRunnable1("InnerRunnable1");
    new InnerRunnable2("InnerRunnable2");
    new ThreadMethod("ThreadMethod").runThread();
    monitor.expect(new String[] {
      "InnerThread1: 5",
      "InnerThread2: 5",
      "InnerThread2: 4",
      "InnerRunnable1: 5",
      "InnerThread1: 4",
      "InnerRunnable2: 5",
      "ThreadMethod: 5",
      "InnerRunnable1: 4",
      "InnerThread2: 3",
      "InnerRunnable2: 4",
      "ThreadMethod: 4",
      "InnerThread1: 3",
      "InnerRunnable1: 3",
      "ThreadMethod: 3",
      "InnerThread1: 2",
      "InnerThread2: 2",
      "InnerRunnable2: 3",
      "InnerThread2: 1",
      "InnerRunnable2: 2",
      "InnerRunnable1: 2",
      "ThreadMethod: 2",
      "InnerThread1: 1",
      "InnerRunnable1: 1",
      "InnerRunnable2: 1",
      "ThreadMethod: 1"
    }, Test.IGNORE_ORDER + Test.WAIT);
  }
} ///:~
