Sun 的 Thread.join 方法是否因使用 Thread 对象进行同步而被破坏?

发布于 2024-07-18 07:44:25 字数 506 浏览 3 评论 0原文

通过运行测试程序和查看源代码,可以清楚地看出,Sun 实现的该方法并不是简单地为指定线程让出时间,而是实际上首先尝试获取线程对象上的监视器。 具体来说,该方法被实现为“同步”。

请注意,wait 和 notify 方法也需要监视器,但与 join 不同,调用者有责任在调用之前获取监视器,文档中也明确如此说明。 Javadoc 中没有记录 join 依赖于监视器的事实,尽管做出推断可能是很自然的。

文档足够清楚吗?

此外,如果线程由于某种原因无法获取监视器,它将挂起,甚至可能永远挂起。 在等待监视器时,线程是不可中断的,并且不会像文档中所述抛出 InterruptedException。 另一方面,目前还不清楚为什么线程无法获取监视器,除非出现编程错误。

担心显示器争用是否合理?

最后,使超时操作依赖于获取监视器似乎是不合适的,除非可以保证获取监视器的任务本身会超时。

依赖于 join() 的监视器是一个合理的实现吗? 是否有可能以其他方式实现它?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

套路撩心 2024-07-25 07:44:25

Thread.join 调用 wait,这会释放监视器。 由于这意味着“加入”线程也不会阻止任何其他线程调用 join,因此我怀疑这会回答您的大多数其他查询。 它不会阻止另一个调用者在线程的监视器上同步(公共监视器的乐趣),但这意味着常见情况工作正常。

只是为了证明您的第一点是错误的,这里有一个示例,它创建 10 个线程,每个线程在主线程上等待 5 秒。 (请忽略可怕的异常吞噬和滥用Date。它仅用于研究线程行为。)

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) {}
        }
    }
}

如果运行它,您将看到所有线程开始和结束等待几乎同时。 如果你的担忧是有根据的,你就不会像你那样得到“交错”的结果。

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.

一桥轻雨一伞开 2024-07-25 07:44:25

很明显,Sun 实现的该方法并不是简单地为指定线程让出时间,而是实际上它首先尝试获取线程对象上的监视器。

它不会屈服于加入的线程,它只是等待,假设线程在某个时刻将运行完成。 在线程上使用 join() 并不会使该线程比任何其他准备运行的线程更有可能运行。

  1. 如果 N 个线程都尝试加入同一个线程,并且它们都指定相同的超时 T,则其中一个线程最终将等待至少 N*T 毫秒。 换句话说,每个线程必须“轮流等待”来执行其等待。 单独的线程串行而不是并行执行连接是否合理?

线程被设计为同时工作。 如果他们都等待,他们会同时等待。 等待线程不会使另一个线程等待更长时间。

.2。 进入具有非零超时的连接的线程可能永远不会返回。

除非您希望这种情况发生,否则不会。

这是因为无法保证监视器永远可用。

您建议的情况只有在线程获得该线程的锁然后永远持有它而无需等待时才会发生。 这是一个编程错误。 恕我直言,您永远不应该直接获取线程对象的锁。

如果线程在阻塞 I/O 操作之前获取自己的监视器,并且该操作挂起,则任何尝试加入该线程的线程也将挂起。

Java 不能保护您免受您自己的 JVM 中的恶意代码的侵害。

具有明确超时的操作无限期挂起是否合理?

如果它被无限期锁定,是的。

.3。 为了编写使用该方法的正确程序,调用者必须提前知道目标线程或其他线程是否可以持有监视器。

永远不要锁定线程对象,没有理由需要这样做,并且不会遇到此问题。 如果您想开始寻找可能使其他开发人员或您自己感到困惑的各种方法,那么恕我直言,Threads 不是您开始的地方。

例如,考虑一下如果线程 1 正在执行某种处理工作,然后线程 2 以超时 0 加入线程 1,然后线程 3 尝试以超时 10 毫秒加入线程 1,会发生什么情况。 0超时连接意味着线程2将等待,直到线程1退出。 但是线程 3 无法开始等待,直到线程 2 释放监视器为止,

线程 2 一旦调用 wait 就释放监视器。 它不能同时等待和保持监视器。

因此,线程 3 的 10 毫秒等待实际上被默默地转换为无限期等待,这不是调用者想要的。

不。 请参阅之前的评论。

要求调用者了解实现细节,这不违反封装原则吗?

它会。

.4。 如果一个线程因为无法获取监视器而被阻塞,那么它是不可中断的,并且不会像文档中描述的那样抛出InterruptedException。 因此,线程不仅可能等待比预期更长的时间,甚至无限期地等待,它还可能变得完全无响应,导致整个程序挂起。 可中断操作变得无响应是否合理?

是的。 但这是一种非常罕见的情况。 如果您使用编写的代码,您所指的情况最多只会存在几毫秒。

总体而言,使超时操作依赖于获取监视器似乎是不合适的,除非可以保证获取监视器的任务本身会超时。 线程连接是否损坏?

您可以使用最新的 Java 5 并发库执行您的建议。

但是,我建议您不要假设超时保证精确到毫秒。 此方法使用的 currentTimeMillis() 在 Windows XP 上仅精确到约 16 毫秒,并且等待/睡眠通常比 Linux 上小超时应有的时间长 2 毫秒。

恕我直言,如果您需要超过 40 毫秒的精度,您可能会遇到困难,但是如果您解决这个问题,您会发现这不一定是问题。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文