import java.util.concurrent.locks.*;

public class Teste1 implements Runnable {
  int qual;
  static UmaDeCadaVezV2 a = new UmaDeCadaVezV2();
  
  public Teste1(int i){
    qual = i;
  }
  public void run() {
    while (true){
      //System.out.println("Tentou entrar " + qual);      
      if (qual == 1) 
        a.entra1();
      else if (qual == 2)
        a.entra2();
      else
        a.entra3();

     }
  }

  public static void main(String[] args) {

    Teste1 tt1 = new Teste1(1);
    new Thread(tt1).start();
    Teste1 tt2 = new Teste1(2);
    new Thread(tt2).start();
    Teste1 tt3 = new Teste1(3);
    new Thread(tt3).start();  new Thread(tt3).start();  new Thread(tt3).start();  
    new Thread(tt3).start();  new Thread(tt3).start();  new Thread(tt3).start();  
  }
}
class UmaDeCadaVez {
    private Object obj = new Object();
    int vez = 1;
    
    public void entra1(){
      synchronized (obj){
        try {
           while (vez != 1){        
            obj.wait();
          }
          vez = 2;
          System.out.println("Entrou " + 1);
          Thread.sleep(100);
          obj.notifyAll();
        } catch (Exception e) { e.printStackTrace();}
      }
    }
    public  void entra2(){
      synchronized (obj){
        try {
          while (vez != 2){
            obj.wait();
          }            
          vez = 3;
          System.out.println("Entrou " + 2);
          Thread.sleep(100);
          obj.notifyAll();
        } catch (Exception e) { e.printStackTrace();}
      }    
    }
    public void entra3(){
      synchronized (obj){
        try {
            while (vez != 3){
              obj.wait();
          }            
          vez = 1;
          System.out.println("Entrou " + 3);
          Thread.sleep(100);
          obj.notifyAll();
        } catch (Exception e) { e.printStackTrace();}
      }    
    }
}    
class UmaDeCadaVezV2 {
    private Object obj = new Object();
    private Object f1 = new Object(); int wf1 = 0; int aviso1 = 1;
    private Object f2 = new Object(); int wf2 = 0; int aviso2 = 0;
    private Object f3 = new Object(); int wf3 = 0; int aviso3 = 0;
    int vez = 1;
    
    public void entra1(){
      boolean espera = false;
      try {
        synchronized (obj){
          if ((vez != 1) || (wf1 > 0)){        
            espera = true;
          }
            wf1++;
        }
        //Thread.yield();
        synchronized (f1){
          if (espera && (aviso1 == 0)){
            f1.wait();
          }
          aviso1--;
        }
        synchronized (obj){
          System.out.println("Entrou " + 1);
          Thread.sleep(200);
          vez = 2;
        }
        synchronized (f2){
          f2.notify();
          aviso2++;
          wf1--;
        }
      } catch (Exception e) { e.printStackTrace();}
    }
    public  void entra2(){
      boolean espera = false;
      try {
        synchronized (obj){
          if ((vez != 2) || (wf2 > 0)){        
            espera = true;
          }
            wf2++;
        }
        Thread.sleep(1000);
        synchronized (f2) {
          if (espera && (aviso2 == 0)){
            f2.wait();
          }
          aviso2--;
        }
        synchronized (obj){
          System.out.println("Entrou " + 2);
          Thread.sleep(200);
          vez = 3;
        }
        synchronized (f3){
          f3.notify();
          aviso3++;
          wf2--;
        }
      } catch (Exception e) { e.printStackTrace();}
    }
    public void entra3(){
      boolean espera = false;
      try {
        synchronized (obj){
          if ((vez != 3) || (wf3 > 0)){        
            espera = true;
          }
            wf3++;
        }
        synchronized (f3) {
          if (espera && (aviso3 == 0)) {
            f3.wait();
          }
          aviso3--;
        }
        Thread.yield();
        synchronized (obj){
          System.out.println("Entrou " + 3);
          Thread.sleep(200);
          vez = 1;
        }
        synchronized (f1){
          f1.notify();
          aviso1++;
          wf3--;
        }
      } catch (Exception e) { e.printStackTrace();}
    }
}   

class UmaDeCadaVezV3 {
  Lock lock = new ReentrantLock();
  int vez = 1;
  Condition cond = lock.newCondition();
  
    public void entra1(){
      lock.lock();
        try {
           while (vez != 1){        
            cond.await();
          }
          vez = 2;
          System.out.println("Entrou " + 1);
          Thread.sleep(100);
          cond.signalAll();
        } catch (Exception e) { e.printStackTrace();}
        finally { lock.unlock();}
      }
    public  void entra2(){
      lock.lock();
        try {
          while (vez != 2){
            cond.await();
          }            
          vez = 3;
          System.out.println("Entrou " + 2);
          Thread.sleep(100);
          cond.signalAll();
        } catch (Exception e) { e.printStackTrace();}
        finally { lock.unlock();}
    }    
    public void entra3(){
      lock.lock();
        try {
            while (vez != 3){
              cond.await();
          }            
          vez = 1;
          System.out.println("Entrou " + 3);
          Thread.sleep(100);
          cond.signalAll();
        } catch (Exception e) { e.printStackTrace();}
        finally { lock.unlock();}
    }    
}
