views:

406

answers:

2

By both running test programs and looking at source code, it is clear that the method, as implemented by Sun, does not simply yield time to the specified thread, but actually it first attempts to obtain a monitor on the thread object. Specifically, the method is implemented as "synchronized."

Note that the wait and notify methods also require the monitor, but unlike join, it is the caller's responsibility to obtain the monitor before making the call, and the documentation clearly says so. The fact that join depends on the monitor is not documented in the Javadocs, although perhaps it is natural to make the inference.

Is the documentation clear enough?

Additionally, if the thread can't obtain the monitor for some reason, it will hang, maybe forever. While waiting for the monitor, a thread is not interruptible, and will not throw the InterruptedException as described in the documentation. On the other hand, it is not clear why a thread wouldn't be able to obtain the monitor except in the case of a programming error.

Is it reasonable to worry about contention over the monitor?

Finally, it seems inappropriate to make the operation of a timeout dependent on obtaining a monitor, unless it can be guaranteed that the task of obtaining the monitor will itself time out.

Is depending on the monitor for join() a reasonable implementation? Is it even possible to implement it any other way?

Note: this question has changed considerably since originally written. It sounded like a troll, and contained arguments which were unconvincing at best.

+9  A: 

Thread.join calls wait, which releases the monitor. As this means a "joining" thread doesn't block any other thread from calling join as well, I suspect this answers most of your other queries. It doesn't prevent another caller from synchronizing on the thread's monitor (oh the joys of public monitors) but it means that the common case works fine.

Just to demonstrate that your first point is wrong, here's an example which creates 10 threads which each wait on the main thread for 5 seconds. (Please ignore the horrible exception swallowing and abuse of Date. It's only intended to be used to study the threading behaviour.)

import java.util.*;

public class Test
{
    public static void main(String[] args) throws Exception
    {
        for (int i=0; i < 10; i++)
        {
            new Thread(new ThreadJoiner(Thread.currentThread(), i))
                     .start();
        }
        try
        {
            Thread.sleep(10000);
        }
        catch (InterruptedException e) {}
    }

    private static class ThreadJoiner implements Runnable
    {
        private final Thread threadToJoin;
        int id;

        public ThreadJoiner(Thread threadToJoin, int id)
        {
            this.threadToJoin = threadToJoin;
            this.id = id;
        }

        public void run()
        {
            try
            {
                System.out.println("Thread " + id +
                                   " waiting at " + new Date());
                threadToJoin.join(5000);
                System.out.println("Thread " + id +
                                   " finished waiting at " + new Date());
            }
            catch (InterruptedException e) {}
        }
    }
}

If you run that, you'll see that all the threads start and end their waits virtually simultaneously. You don't get the ends "staggered" as you would if your concerns were well-founded.

Jon Skeet
Thanks for taking the time to write some code. You are right, and much of the original question was wrongheaded. I started thinking about one issue, namely the issue of a thread holding the monitor for some reason other than to simply wait, join, or notify, and that spun off some examples which didn't make much sense.The point was really to ask whether the implementation lives up to the guarantee that the method will time out. It seems to depend on things it shouldn't, like what other threads are doing.
+3  A: 

it is clear that the method, as implemented by Sun, does not simply yield time to the specified thread, but actually it first attempts to obtain a monitor on the thread object.

It doesn't yield to the thread joined, it just waits with the assumption that at some point the thread will run to completion. Having a join() on a thread does not make it more likely to run than any other thread ready to run.

  1. If N threads all attempt to join the same thread, and they all specify the same timeout T, then one of the threads will end up waiting at least N*T ms. In other words, each thread has to "wait its turn" to execute its wait. Is it reasonable for separate threads to execute joins serially instead of in parallel?

Threads are designed to work concurrently. If they all wait, they do so concurrently. A waiting thread does not make another thread wait longer.

.2. It is possible that a thread which enters a join with a nonzero timeout may never return.

not unless you intend this to happen.

This is because there is no guarantee the monitor will ever become available.

The situation you suggest could only occur if a thread obtains a lock on the thread and then holds it forever without waiting. This is a programming bug. IMHO You should never obtain a lock on a thread object directly.

If a thread obtains its own monitor before blocking on an I/O operation, and that operation hangs, then any thread which attempts to join the thread will also hang.

Java doesn't protect you from malicious code in your own JVM.

Is it reasonable for an operation with an explicit time out to hang indefinitely?

If it is being locked out indefinitely, yes.

.3. In order to write a correct program which uses the method, the caller must know in advance whether the target thread or some other thread could be holding the monitor.

Don't ever lock the thread object, there is no reason you should need to, and you won't have this problem. If you want to start looking at every way you could confuse other developers or yourself, then Threads isn't the place you would start IMHO.

For example, consider what happens if thread 1 is performing some sort of processing work, then thread 2 joins thread 1 with a timeout of 0, and then thread 3 attempts to join thread 1 with a timeout of 10 ms. The 0 timeout join means that thread 2 will wait until thread 1 exits. But thread 3 cannot begin its wait until thread 2 releases the monitor,

Thread 2 releases the monitor as soon as wait is called. It cannot wait and hold the monitor at the same time.

so effectively thread 3's wait of 10 ms was silently converted to an indefinite wait, which was not what the caller intended.

no. see previous comments.

Doesn't requiring the caller to know details about the implementation violate the principle of encapsulation?

It would.

.4. If a thread is blocked because it cannot obtain the monitor, it is not interruptible, and will not throw the InterruptedException as described in the documentation. So not only might a thread wait longer than expected, or even indefinitely, it may become completely unresponsive, causing the entire program to hang. Is it reasonable for an interruptible operation to become unresponsive?

yes. But this is a very rare condition. If you use the code as written, the situation you refer to will only exist for a few milli-seconds at most.

Overall, it seems inappropriate to make the operation of a timeout dependent on obtaining a monitor, unless it can be guaranteed that the task of obtaining the monitor will itself time out. Is thread join broken?

You can do what you suggest with the more recent Java 5 concurrency libraries.

However, I suggest you shouldn't assume that timeouts are guaranteed to be milli-second accurate. currentTimeMillis() used by this method is only accurate to about 16 ms on Windows XP and wait/sleep is typically 2 ms longer than it should be for small timeouts on Linux.

IMHO If you need accuracy of more than 40 ms you may have difficulty, however if you work around this you will find this doesn't have to be a problem.

Peter Lawrey