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.