//TeaOK.java --- using Thread safely --- by tsaiwn@csie.nctu.edu.tw
//According to the reference manual in JDK, the function stop( ) is unsafe.
//Thus, we should "tell" the thread to die through a volatile variable.
//..And, any thread should test the associated variable to see if it should
//..finish it's job immediately.
public class TeaOK {
    protected static long nTicket = 0;
    protected static String lock = "Just a Lock for Critical section";
    public static void main(String[]x) throws Exception {
        Tea t1sub = new Tea();  // note that Tea is not a Thread, ..
        Thread t1 = new Thread(t1sub,"ArBian"); // wrap it in a Thread
        Coffee t2 = new Coffee("Chang-3"); Coffee t3 = new Coffee("Lee-4");
        t1.start( ); t2.start( ); t3.start( );
    // t2 和 t3 都是 Coffee, 因Coffee extends Thread, 故t2, t3也都是 Thread
        while(true) {
           Thread.sleep(13);   
           if(nTicket > 18) {    // close the store, abort all threads
              t1sub.shouldDie = true;  //Note the usage  //t1.stop( );     
              // t1sub是Tea, 它不是 Thread, 因它只是implements Runnable
              //注意 t1 是把 t1sub 包成 Thread, 所以可用 t1.start()
              //但Thread這種東東沒有shouldDie, 因此用 t1.shouldDie 不對
              t2.shouldDie = true;  t3.shouldDie = true;  //t3.stop( );
              break;
           }
        } // while
        t1.join( ); t2.join( ); t3.join( ); // wait for them to finish
        System.out.println("\nThank you and Bye!");
    } // main
} // class TeaOK
class Coffee extends Thread {
    protected volatile boolean shouldDie = false;
    public Coffee(String name) {super(name); }  //setName for this thread
    public void run( ) {  int total = 0;
        System.out.println("@ " + getName() + "  entered.");
        while(true) {
          synchronized(TeaOK.lock)  // comment out this line to test
          {             // 故意刪掉上列 synchronized 再run看看就可看出問題
           long n = TeaOK.nTicket + 1;
           print(n); print(" Drink Coffee by "+getName()+"\n"); total++;
           yield( );   // yield the CPU on purpose! 讓出CPU突顯同步問題
           TeaOK.nTicket = n;  
          }
           // Thread 都應該隨時check主人是否要我們自我了斷, 是就離開 run()
           if(this.shouldDie == true) break; // return;
           //以下故意再 delay 一下, 使同時access同一變數的問題更明顯
           try{ sleep( (int)(Math.random() *58)); }catch(Exception e){;}
        } // while
        System.out.println("== " + getName() + " is leaving... " +total);
    } // run
    static void print(String s){ System.out.print(s); }
    static void print(long n)
      { if(n<100)print(" "); if(n<10)print(" "); print(" "+n); }
} // class Coffee
class Tea implements Runnable {    // Tea 表面上不是 Thread
    protected volatile boolean shouldDie = false;
    public void run( ) { int total = 0;
        String name = Thread.currentThread( ).getName( ); //Thread 有getName()
        System.out.println("@@ " + name + " entered.");
        while(true) {
          synchronized(TeaOK.lock) //Critical section monitored by TeaOK.lock
          {         //用 TeaOK.lock 確保 Critical section 內只能有一 thread
           long n = TeaOK.nTicket + 1;
           Coffee.print(n); Coffee.print(" Drink Tea by "+name+"\n"); total++;
           Thread.yield( );   // 此例 Tea 不是 Thread 故不能直接寫 yield( );
           TeaOK.nTicket = n;    // 須 extends Thread 者才是 Thread
          }
           if(shouldDie == true) break; // 主人要我死, 我就只好 bye bye
           try{Thread.sleep((int)(Math.random()*58));}catch(Exception e){;}
        } // while...注意不是 Thread 者也不能直接用 sleep, 要用 Thread.sleep
        System.out.println("=-= " + name + " is leaving... "+total);
    } // run
} // class Tea
