Question

public class ThreadStarvation implements Runnable{
         long startTime=System.currentTimeMillis();
         Timer t;

    class RunnerTask extends TimerTask  {

      public void run(){
        if((System.currentTimeMillis()- startTime)< 100000){
           System.out.println(Thread.currentThread().getName()+": in timer  task run() method"+" : "+Calendar.getInstance().getTime());

       }
    else
        t.cancel();
      }
   }

public synchronized void synchronizedTimerMethod(){

try{
    Thread.sleep(1000);
    t=new Timer();
    t.schedule(new RunnerTask(), 0, 2000);
}
   catch(InterruptedException ie){}
}

public void run(){
    synchronizedTimerMethod();

}

public static void main(String[] args) {

            ThreadStarvation ts1=new ThreadStarvation();
            Thread t1=new Thread(ts1);
            Thread t2=new Thread(ts1);
            t1.start();
            t2.start();
  }
}

Three doubts is there in above program :

1) What I want from this program was that when thread t1 starts, goes to synchronized method synchronizedTimerMethod() will lock object ts1 and should locked it until timer object breached the condition. Hence Thread t2 will unable to gain regular access to shared resources ts1 and is unable to make progress and will go into starvation. But this is not happening. Is it not possible with Timer class ? I am novice to it.

2) When thread t1 starts, it goes to synchronized method synchronizedTimerMethod and the object ts1 will be locked. The object ts1 will not be released until timer object is scheduled till condition is breached. But what is happening thread t1 timer object first scheduled start at that time only Thread t2 enters synchronized method synchronizedTimerMethod. Is object ts1 got released due to Timer run method?

3) Also When both thread breached the condition the task is not cancelled, in particular the program stucks or I think it goes into deadlock. WHY?

I rewritten my code as below :

   public class ThreadStarvation implements Runnable{
       long startTime=System.currentTimeMillis();
       final Timer t=new Timer;

class RunnerTask extends TimerTask  {

  public void run(){
    if((System.currentTimeMillis()- startTime)< 100000){
       System.out.println(Thread.currentThread().getName()+": in timer  task run() method"+" : "+Calendar.getInstance().getTime());

   }
else
    t.cancel();
  }
}

public synchronized void synchronizedTimerMethod(){

try{
  Thread.sleep(1000);
  t.schedule(new RunnerTask(), 0, 2000);
}
  catch(InterruptedException ie){}
}

public void run(){
    synchronizedTimerMethod();

 }

public static void main(String[] args) {

        ThreadStarvation ts1=new ThreadStarvation();
        Thread t1=new Thread(ts1);
        Thread t2=new Thread(ts1);
        t1.start();
        t2.start();
  }
}

Now I just want that the task should get stopped. For that I made the Timer object as final. Then also the task don't seem to cancel. Is some more modification is needed ? Please help.

Was it helpful?

Solution

1) If you want to enforce fact that t1 enters before t2, then you can't depend on Timer (or rather time) to ensure this (arbitrary interleaving). You should rewrite as a Monitor with a barrier (condition) that only permits t1 to enter first. Then make t1 never release the lock to starve t2 i.e. prevent your synchronized method from terminating (see here).

What is a monitor and how do I create one?
In concurrency, it is a construct used to synchronize your program and make it more predictable i.e. t1 before t2 as illustrated. In this context, synchronization is based on certain conditions/states being satisfied. These conditions act as "barriers" which either prevent or allow a thread to execute. This is very useful as we can use such barriers to not only make our program mode predictable, but also allow us to guarantee certain desirable properties in concurrency i.e. fairness, avoiding deadlock etc. Hence the importance of monitors.

In Java we can create a monitor by defining a class which contains the barrier conditions as private variables. We then only allow changes to those variables through synchronized methods that first test whether the conditions have been fulfilled (barrier).

A simple example to illustrate based on simplifications to your code:

public class ExampleMonitor implements Runnable{
    // Condition for our barrier, note it is private
    private boolean t1Entered = false;

    public synchronized void synchronizedTimerMethod(){
        // Test the barrier (check if conditions hold)
        while (!t1Entered && !Thread.currentThread().getName().equals("t1")) { 
            try {
                // Did not pass barrier so wait and release lock
                wait();
            } catch (Exception e) {
                // Handle
            }
        }
        // Thread passed barrier and has acquired the lock and can do what it wants

        // Update condition so now anyone can enter/pass the barrier
        t1Entered = true;

        // If this method never terminates then no other thread can enter because lock is never released
        long enterTime = System.currentTimeMillis();
        while (true) {
            System.out.println(Thread.currentThread().getName());

            // Let's allow the method to return and thus release the lock after fixed amount of time
            // We can then see that threads other than t1 can now acquire the lock
            if (System.currentTimeMillis() - enterTime > 5000) {
                break;
            }
        }
        // Notify/wake up any waiting threads
        this.notifyAll();
    }

    public void run(){
        synchronizedTimerMethod();
        // Thread will now terminate
    }

    public static void main(String[] args) throws InterruptedException {
        ExampleMonitor ts1 = new ExampleMonitor();

        Thread t1=new Thread(ts1);
        t1.setName("t1");

        Thread t2=new Thread(ts1);
        t2.setName("t2");

        t2.start();
        // To illustrate how Monitors can be used to ensure 
        //  ordering despite the order threads start in
        Thread.sleep(2000);
        t1.start();
    }
}

Note: this is just an quick example to illustrate and is not ideal i.e. you should not define a monitor that implements Runnable. You can read more about monitors here. Also I recommend working through following book which I also used.

2) See immibis' thorough answer.

3) From the Java doc:

After the last live reference to a Timer object goes away and all outstanding tasks have completed execution, the timer's task execution thread terminates gracefully (and becomes subject to garbage collection). However, this can take arbitrarily long to occur. By default, the task execution thread does not run as a daemon thread, so it is capable of keeping an application from terminating. If a caller wants to terminate a timer's task execution thread rapidly, the caller should invoke the timer's cancel method.

OTHER TIPS

public synchronized void synchronizedTimerMethod(){

try{
    Thread.sleep(1000);
    t=new Timer();
    t.schedule(new RunnerTask(), 0, 2000);
}
   catch(InterruptedException ie){}
}

This doesn't do what you think it does. The synchronized method waits for one second, then schedules a RunnerTask to happen every two seconds, then returns. Note that it does not wait for the RunnerTask to run. ts1 is locked only until synchronizedTimerMethod returns, i.e. for one second.

When the "condition is breached" after 100 seconds, you only cancel one Timer, because of a bug. Notice that synchronizedTimerMethod sets t to a new Timer, and that there is only one variable t. After the first task is scheduled, t is a Timer - call it Timer 1. After the second task is scheduled, t is a different Timer - call it Timer 2. Then when 100 seconds is up, both tasks cancel t, which cancels Timer 2 twice.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top