0

As a junior Java developer, while writing my first multi-threaded code, I remember wanting to use Thread.join(millis) in a loop, and logging each time the thread was still alive. I was surprised to see that join returns without letting me know the reason for returning, but considered it to be some kind of a native "magic" that a noob such as I was cannot understand.

Lately I had another look at java.lang.Thread implementation, and as I was reading join's implementation I was wondering again - since it merely waits on isAlive condition, wouldn't it be natural to use the last state of the thread as the return value, so that client code does not have to check it explicitly outside?

    /**
     * Waits at most {@code millis} milliseconds for this thread to
     * die. A timeout of {@code 0} means to wait forever.
     *
     * <p> This implementation uses a loop of {@code this.wait} calls
     * conditioned on {@code this.isAlive}. As a thread terminates the
     * {@code this.notifyAll} method is invoked. It is recommended that
     * applications not use {@code wait}, {@code notify}, or
     * {@code notifyAll} on {@code Thread} instances.
     *
     * @param  millis
     *         the time to wait in milliseconds
     *
     * @throws  IllegalArgumentException
     *          if the value of {@code millis} is negative
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final synchronized void join(long millis)
    throws InterruptedException {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
    }

I guess the answer might be a plain "Yes, it is possible, and you should ask the original JDK implementer.", but I am hoping for a more educating answer, if such one exists.

Elist
  • 5,313
  • 3
  • 35
  • 73
  • Because this is how it was designed way back when. Why? Because this requirement wasn't anticipated. Why can't they change it? Because a) existing implementations require it to be void; b) you have no way of judging how useful this is more generally, so it is unclear whether adding this feature to the API would carry its weight. How hard is it to call `isAlive()` after join returns? – Andy Turner Feb 15 '18 at 07:49
  • This is a legit answer, except for a) - correct me if I'm wrong - changing `void` to something else cannot break existing implementation (maybe only produce warnings). – Elist Feb 15 '18 at 07:52
  • 1
    that would violate binary compatibility. – Andy Turner Feb 15 '18 at 07:59

2 Answers2

2

wouldn't it be natural to use the last state of the thread as the return value

"Natural" isn't the word I'd use, but it would have been a reasonable choice. There were many reasonable choices.

Remember that there are some really unnatural bits of API design in Java. For example, java.util.Date takes/returns the number of years after 1900 to represent the year, not the year. Similarly, representing January as month 0 and December as month 11.

Who knows how many hours of developer time have been wasted because of those choices. At least the fact that Thread.join(long) is void doesn't mislead you; you just have to put in a little more work to find out what you want to know.

The APIs were designed a) a long time ago, b) with a need to ship "something". They're not perfect, because they were designed without the benefit of hindsight. It's really hard to design APIs correctly, and even when you have done, somebody will come along and say "I don't like it because it doesn't do X", or "I'd prefer it to do Y".

Unless the API actually proves to be deficient - which Thread.join(long) isn't, because you can just call Thread.isAlive() when it returns - or broken, there is no real incentive to fix it, especially when you have a strong commitment to preserving backwards compatibility like Java does.

Andy Turner
  • 137,514
  • 11
  • 162
  • 243
1

It could be anything from not deviating from Thread.join() too much to not wanting to return a possibly stale value of isAlive() to not spending too much time on such an insignificant detail choice.

Considering it's from 1.0, you're judging this from an entirely different standpoint. It's not changed because of backwards compatibility (binary if not source) and mainly: why should they change it? It's not broken.

Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • Your hint regarding binary compatibility led me to this answer: https://stackoverflow.com/a/3589948/1609201. This is indeed educating, thanks. – Elist Feb 15 '18 at 08:06