颠覆我的Thread.join()

学而时习之,不亦说乎!

                             --《论语》

  为什么说是颠覆?

  1)任何对象都可以作为锁对象,锁对象的行为都是一样的吗?之前我一直认为锁对象的方法都是定义在Object类中,而所有类都是Object的子类,这些方法又都是native方法,那么用哪个对象作为锁对象又有什么区别呢?

  2)一个线程对象a在run()方法内部调用线程对象b的join()方法,那么是将b线程加入,等到b线程执行完毕再执行a线程?那么如果还有一个正在执行的c线程呢,线程c也会等待b执行完吗?

代码1:

package com.zby;

public class Application1 {

    public static void main(String[] args) {
        Thread prepare = new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("Hello,World!-----" + i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        prepare.start();
        System.out.println("Hello,ZBY!");
    }

}

控制台输出:

Hello,ZBY!
Hello,World!-----0
Hello,World!-----1
Hello,World!-----2
Hello,World!-----3
Hello,World!-----4

结果不难分析,主线程直接执行,而prepare线程虽然启动了,但是执行没那么快,所以后执行了。但是我的prepare是进行准备工作的,我想让prepare线程执行完毕后再执行主线程。

代码2:

package com.zby;

public class Application2 {

    public static void main(String[] args) {
        Thread prepare = new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("Hello,World!-----" + i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        prepare.start();
        try {
            prepare.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("Hello,ZBY!");
    }
}

控制台输出:

Hello,World!-----0
Hello,World!-----1
Hello,World!-----2
Hello,World!-----3
Hello,World!-----4
Hello,ZBY!

很小儿科,加了一个一行代码:prepare.join();要是之前我会理解成把prepare加入到主线程先执行,执行完才能执行其它线程。然而,非也。

Thread.join()源代码:

    public final void join() throws InterruptedException {
        join(0);
    }
    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;
            }
        }
    }

这儿可以看出来,我们调用prepare.join()的时候发生了什么:把prepare作为锁对象,调用锁对象的wait(0)方法,阻塞当前线程!也就是说,并不是把需要执行的线程加入进来让他先执行,而是阻塞当前的线程!那么,如果还有第三个线程也在执行,那么prepare线程是不会一直执行,而是跟第三个线程抢CPU执行权。总结起来就是,其实就是阻塞了当前的线程,对于其他线程都是没有影响的。感兴趣可以加入第三个线程自己测试一下。

等价于代码2的代码3:

package com.zby;

public class Application3 {

    public static void main(String[] args) {
        Thread prepare = new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("Hello,World!-----" + i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        prepare.start();
        synchronized(prepare){
            try {
                prepare.wait(0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Hello,ZBY!");
    }
}

控制台输出:

Hello,World!-----0
Hello,World!-----1
Hello,World!-----2
Hello,World!-----3
Hello,World!-----4
Hello,ZBY!

这儿就解决了第二个问题,join()方法其实不是把调用的线程加入进来优先执行,而是阻塞当前线程!

看完代码3就有疑问了,prepare.wait(0);自然没错,阻塞住了主线程。但是并没有任何地方调用notify或者notifyAll方法,线程不是应该一直阻塞么,怎么会在prepare执行完后继续执行主线程代码了?

这就是第一个问题了,普通对象当然是wait后必须等待notify唤醒才能继续执行,但是Thread对象呢?具体的我也不知道,但是从这儿可以推论出,thread对象在执行完毕后,自动唤醒了!那么到底是notify还是notifyAll呢?那么,多启动一个线程,并使用prepare对象作为锁对象,调用wait方法。

代码4:

package com.zby;

public class Application3 {

    public static void main(String[] args) {
        final Thread prepare = new Thread(new Runnable() {
            public void run() {
                for (int i = 0; i < 5; i++) {
                    System.out.println("Hello,World!-----" + i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        prepare.start();
        Thread ready = new Thread(new Runnable() {
            public void run() {
                synchronized (prepare) {
                    try {
                        prepare.wait(0);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                for (int i = 0; i < 10; i++) {
                    System.out.println("Hello,Earth!-----" + i);
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        ready.start();
        synchronized (prepare) {
            try {
                prepare.wait(0);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("Hello,ZBY!");
    }
}

控制台输出:

Hello,World!-----0
Hello,World!-----1
Hello,World!-----2
Hello,World!-----3
Hello,World!-----4
Hello,Earth!-----0
Hello,ZBY!
Hello,Earth!-----1
Hello,Earth!-----2
Hello,Earth!-----3
Hello,Earth!-----4
Hello,Earth!-----5
Hello,Earth!-----6
Hello,Earth!-----7
Hello,Earth!-----8
Hello,Earth!-----9

可以看出,在preare执行完之后,自动把ready和main线程都唤醒了!也就是说,使用Thread对象作为锁对象,在Thread执行完成之后,会唤醒使用该对象作为锁对象调用wait()休眠的线程。

总结:

1)使用线程的join方法,是将调用的线程对象作为锁对象,阻塞当前线程,不影响其他线程的运行。

2)推论:Thread对象作为线程锁对象,会在Thread对象执行完后,调用Thread对象的notifyAll方法。

时间: 2024-10-25 19:46:57

颠覆我的Thread.join()的相关文章

java中synchronize锁 volatile thread.join()方法的使用

对于并发工作,你永远不知道一个线程何时运行,你需要某种方式来避免两个任务访问相同的资源,即要避免资源竞争,至少在关键代码上不能出现这样的情况,否则多个线程同时对某个内存区域操作会导致数据破坏. 程序代码中的临界区是需要互斥访问的,同一时刻只能有一个线程来访问临界区,也就是线程对临界区的访问时互斥的. 竞争条件:当多个线程同时访问某个共享的内存区域并且对其进行读写操作时,就会出现数据破坏.这就是竞争条件.避免竞争条件的方法是synchronized加锁. 样例,设有一个现成,该线程的任务是对共享变

thread.join函数,java多线程中的join函数解析

join函数的作用,是让当前线程等待,直到调用join()的 线程结束或者等到一段时间,我们来看以下代码 1 package mian; 2 3 4 public class simpleplela { 5 static void threadMessage(String message) { 6 String threadName = 7 Thread.currentThread().getName(); 8 9 System.out.println(threadName+" "+m

java多线程同步以及线程间通信详解&amp;消费者生产者模式&amp;死锁&amp;Thread.join()(多线程编程之二)

本篇我们将讨论以下知识点: 1.线程同步问题的产生 什么是线程同步问题,我们先来看一段卖票系统的代码,然后再分析这个问题: [java] view plain copy print? package com.zejian.test; /** * @author zejian * @time 2016年3月12日 下午2:55:42 * @decrition 模拟卖票线程 */ public class Ticket implements Runnable { //当前拥有的票数 private 

thread.Join(); 让主线程等待自己完成

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading; namespace ConsoleApplication1 { class Program { private static void Method() { Thread.Sleep(5000); Console.WriteLine("当前线程:" + Thread.C

Thread.join()的使用

代码清单: package com.baidu.nuomi.concurrent; import java.util.concurrent.TimeUnit; /** * Created by sonofelice on 16/6/18. */ public class Join { public static void main(String[] args) throws Exception{ Thread previous = Thread.currentThread(); for (int

C++11 thread::join(4)

原文地址:http://www.cplusplus.com/reference/thread/thread/join/ public member function <thread> std::thread::join void join(); Join thread The function returns when the thread execution has completed. 当该线程执行完成后才返回.(即等待子线程执行完毕才继续执行主线程) This synchronizes

C#多线程Thread.Join()的详解

class TestThread { private static void FirstThreadFun() { for (int i = 0; i < 10; i++) { Console.WriteLine(Thread.CurrentThread.Name + " i = " + i); } Console.WriteLine(Thread.CurrentThread.Name + " 执行完毕"); } static void Main(string

Thread.join()分析方法

API: join public final void join() throws InterruptedException 等待该线程终止. 抛出: InterruptedException - 假设不论什么线程中断了当前线程.当抛出该异常时,当前线程的中断状态 被清除. join public final void join(long millis) throws InterruptedException 等待该线程终止的时间最长为 millis 毫秒.超时为 0 意味着要一直等下去. 參数

thread.join 从异步执行变成同步

Java的线程模型为我们提供了更好的解决方案,这就是join方法.在前面已经讨论过,join的功能就是使用线程 从异步执行变成同步执行 当线程变成同步执行后,就和从普通的方法中得到返回数据没有什么区别了.因此,可以使用如下的代码更有效地解决这个问题: Java代码 thread.start(); thread.join(); ... 在thread.join()执行完后,线程thread的run方法已经退出了,也就是说线程thread已经结束了.因此,在thread.join()后面可以放心大胆