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

时间:2009-05-01 20:37:31

标签: java multithreading join timeout

通过运行测试程序和查看源代码,很明显,由Sun实现的方法不会简单地为指定的线程产生时间,但实际上它首先尝试在线程对象上获取监视器。具体而言,该方法实现为“同步”。

请注意,wait和notify方法也需要监视器,但与join不同,调用者有责任在进行调用之前获取监视器,文档清楚地说明了这一点。 Javadocs中没有记录连接取决于监视器的事实,尽管可能很自然地做出推断。

文档是否足够清晰?

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

担心监视器争用是否合理?

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

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

2 个答案:

答案 0 :(得分:10)

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

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

答案 1 :(得分:5)

  

很明显,Sun实现的方法不会简单地为指定的线程产生时间,但实际上它首先尝试在线程对象上获取监视器。

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

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

线程旨在同时工作。如果他们都等待,他们会同时这样做。等待线程不会使另一个线程等待更长时间。

  

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

除非你打算这样做,否则

  

这是因为无法保证显示器可用。

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

  

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

Java不会保护您免受自己JVM中的恶意代码的攻击。<​​/ p>

  

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

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

  

0.3。为了编写使用该方法的正确程序,调用者必须事先知道目标线程或某个其他线程是否可以保持监视器。

不要锁定线程对象,没有理由你需要,并且你不会遇到这个问题。如果你想开始寻找各种方式,你可能会混淆其他开发人员或你自己,那么Threads不是你开始恕我直言的地方。

  

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

线程2会在调用wait后立即释放监视器。它不能等待并同时握住显示器。

  

如此有效地将线程3的等待10毫秒静默地转换为无限期等待,这不是调用者想要的。

没有。见以前的评论。

  

不要求调用者知道有关实现的细节违反了封装原则吗?

会的。

  

0.4。如果某个线程由于无法获取监视器而被阻塞,则它不可中断,并且不会抛出InterruptedException,如文档中所述。因此,不仅线程等待的时间超过预期,甚至无限期,它可能会完全没有响应,导致整个程序挂起。对于可中断操作没有反应是否合理?

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

  

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

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

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

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