Thread.join 和 Synchronized 有啥区别?

Posted

技术标签:

【中文标题】Thread.join 和 Synchronized 有啥区别?【英文标题】:What is the difference between Thread.join and Synchronized?Thread.join 和 Synchronized 有什么区别? 【发布时间】:2015-01-30 10:04:21 【问题描述】:

我很困惑何时在多线程应用程序中使用Thread.join() 以及何时使用synchronization

据我说,它们都阻塞或等待其他线程完成执行。 此示例必须依次输出 10 个 A、10 个 B 和 10 个 C,例如:

1  : A
2  : A
3  : A
4  : A
5  : A
6  : A
7  : A
8  : A
9  : A
10 : A
1  : B
2  : B
3  : B
4  : B
5  : B
6  : B
7  : B
8  : B
9  : B
10 : B
1  : C
2  : C
3  : C
4  : C
5  : C
6  : C
7  : C
8  : C
9  : C
10 : C
----ProGraM ENDS----

示例从这里开始

class SyncTest extends Thread 
   
    StringBuffer sb;

    public SyncTest(StringBuffer sb) 
    
        this.sb = sb;   
    

    public void run()
    
        synchronized(sb) 
        
            for(int i=1;i<=10;i++)
                System.out.println(i+" : "+sb.charAt(0));
            
            sb.setCharAt(0, (char) (sb.charAt(0)+1));
        
    

    public static void main(String [] args) throws InterruptedException
    
        StringBuffer sb = new StringBuffer("A");
        Thread t1=new SyncTest(sb);
        Thread t2=new SyncTest(sb);
        Thread t3=new SyncTest(sb);

        t1.start();

        t2.start();

        t3.start();

        Thread.sleep(1000);

        System.out.println("----ProGraM ENDS----");
    

在这里,输出结果是 10 个 A,然后是 10 个 B,然后是 10 个 C,按顺序排列。但我也可以使用 Thread.join 而不是 synchronized 块来获得相同的输出,如下所示:

public void run()
    
        //removed synchronized statement...

            for(int i=1;i<=10;i++)
                System.out.println(i+" : "+sb.charAt(0));
            
            sb.setCharAt(0, (char) (sb.charAt(0)+1));

    

    public static void main(String [] args) throws InterruptedException
    
        StringBuffer sb = new StringBuffer("A");
        Thread t1=new SyncTest(sb);
        Thread t2=new SyncTest(sb);
        Thread t3=new SyncTest(sb);

        t1.start();
        t1.join();
        t2.start(); // wait for t1 to complete
        t2.join();
        t3.start(); // wait for t2 to complete
        t3.join(); 

                     // wait for t3 to complete
        System.out.println("----ProGraM ENDS----");
    

谁能消除我对这两种技术使用的困惑,即何时在 Java 的多线程中使用 Thread.join 以及何时使用 synchronization

【问题讨论】:

不要将synchronized 视为等待任何事情。 synchronized 的目的是防止不同的线程同时弄乱相同的数据。如果必须,它等待,但这总是不太理想的结果:在完美的世界中,永远不会争用锁。另一方面,当我们调用join() 时,那是因为我们想要 等待。当 B 完成之前 A 无能为力时,线程 A 调用 B.join()。 【参考方案1】:

Thread.join() 等待线程完全完成,而synchronized 块可用于防止两个线程同时执行同一段代码。

一般来说,很难建议何时使用其中一个而不是另一个,因为它们用于不同的目的。很难找到一个例子,比如你的代码,两者之间的差异很小。

话虽如此,在您的第一个示例中,不能保证输出将按字母顺序排列。您不能确定哪个线程将首先到达synchronized 块。所以在这种特殊情况下,join() 是最合适的。

【讨论】:

同步块不会阻止两个线程同时执行同一段代码。如果这些线程使用不同的监视器(例如,因为它们使用不同的实例),那么它们可以同时运行相同的代码,并且如果多个代码被同一个监视器保护,那么两个线程将无法运行同时不同段代码。 @MarkRotteveel 你说的很对。我已经温和地编辑了我的答案以反映您的评论。但总的来说,我鼓励 OP 阅读该主题,因为甚至比您的评论涵盖的内容还要多。我的回答并不是要完整描述 synchronized 的工作原理。 谢谢,你说得对,同步比我的评论更复杂。但我认为过度简化也是危险的。但是感谢您的更新。 @MarkRotteveel 您能否将我链接到任何详细显示您评论内容的文章。我正在学习多线程,我对完全相同的部分感到困惑。 @NeerajYadav 任何关于Java基础的好书都会解释这一点,否则从Java Tutorial: Concurrency开始【参考方案2】:

thread.join() 停止执行当前线程,直到加入的线程完成。你的评论正确.. :)

同步可防止多个线程在同一个实例上执行同步部分的代码。

【讨论】:

【参考方案3】:

synchronized 关键字启用了一种锁定机制,允许线程不互相踩踏。 Java 文档将其描述为“防止线程干扰和内存一致性错误”的一种方式。

如果你使用join(),它确保一旦一个线程调用join,当前线程(正在运行的线程)将不会执行,除非你调用join的线程完成。我认为下图可能有助于更好地可视化这一点。

Source

【讨论】:

一个线程等待另一个线程完成?请详细说明【参考方案4】:

如果没有join(),线程将并行运行并依赖于操作系统时间片(首先启动)。使用join() 线程串联运行。例如: 假设你有两个线程都调用join() 方法

MyThread t1 = new MyThread();
MyThread t2 = new MyThread();

t1.join(); // this will be start first.
t2.join(); // this will be start only after above.

现在没有join() 方法,t1t2 中的任何一个都可以先启动。没有保证。

synchronized 语句/关键字用于监视线程,因此一次只有一个线程可以访问该同步方法/变量。是否使用join() 并不重要。

如果将synchronizedjoin() 一起使用,则可以保证线程t1 只能先访问。如果没有synchronizedt1t2 线程都可以随时访问,但这些线程会启动和终止。因为join()而连续剧。

【讨论】:

【参考方案5】:

它们显然不一样,但是,如果它们将用于相同的目的(序列化访问/执行),那么同步块可以被认为是比使用连接更灵活的版本,因为它的用法与特定线程无关要序列化执行的实例。

此外,在同步块中,共享数据块的概念比连接更加强调。

【讨论】:

【参考方案6】:

简单示例:

您有一个静态字符串表,其中一些线程将放置一些值。 该表用 "empty" 初始化。使用静态索引访问该表。 如果一个线程放入一个值,它将增加静态索引。

如果你同步线程,它将确保一个线程放置的任何值都不能被另一个线程覆盖。

如果您在线程上使用连接,则只有第一个连接的线程有机会将值放入表中。因为另一个将等待但知道索引增加,他们将无法访问表(null指针异常)。所以 Join 让其他线程没用了。

此示例在包含同步方法的同一实例上使用线程。

【讨论】:

以上是关于Thread.join 和 Synchronized 有啥区别?的主要内容,如果未能解决你的问题,请参考以下文章

浅析Thread.join()

在 JRuby 分析中花费大量时间在 Thread#initialize 和 Thread#join 中意味着啥?

std::thread.join() 做啥?

Thread线程join方法自我理解

17.Thread.join的用法和原理

Java中用CyclicBarrier以及CountDownLatch和join相比有啥不同