Java并发编程笔记

Thread.join()

cThread.join()方法使当前线程阻塞,直到子线程cThread执行完毕后,当前线程才会恢复运行。

实现原理:

  1. join()方法调用了join(0)

    1
    2
    3
    public final void join() throws InterruptedException {
    join(0);
    }
  2. join(long millis)是一个同步方法,最后会通过调用wait()方法挂起当前线程(即调用线程),直到其他线程调用子线程cThreadnotify()或者notifyAll()方法

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    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;
    }
    }
    }
  3. 子线程run()执行完毕后,系统在关闭该子线程前,会调用其exit()方法,继而在ThreadGroup.threadTerminated(Thread t)中唤醒被阻塞的调用线程。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    /**
    * This method is called by the system to give a Thread
    * a chance to clean up before it actually exits.
    */
    private void exit() {
    if (group != null) {
    group.threadTerminated(this);//提心ThreadGroup当前线程已经被终止
    group = null;
    }

    //其他代码 ……
    }

    // ThreadGroup
    void threadTerminated(Thread t) {
    synchronized (this) {
    remove(t);

    if (nthreads == 0) {//线程组线程数为0时
    notifyAll();//唤醒所有等待中的线程
    }
    if (daemon && (nthreads == 0) &&
    (nUnstartedThreads == 0) && (ngroups == 0))
    {
    destroy();
    }
    }
    }

notify()和notifyAll()的区别

先了解两个概念:

锁池:某个Thread调用某个对象的同步方法(synchronized),但还没获取到该对象的锁时,会进入锁池和其他类似的线程一起竞争该对象的锁。

等待池:当某个Thread调用某个对象的wait()方法释放掉该对象的锁进入阻塞后(waiting on this object’s monitor),会进入等待池。等待池中的线程不会竞争该对象的锁。

notify()方法会从等待池中唤醒一个指定线程,该线程可以再次回到锁池竞争该对象的锁,但可能会导致死锁(如果唯一唤醒的线程阻塞了并依赖其他线程唤醒,但其他线程都在等待池无法竞争锁,导致死锁)。

notifyAll()方法则会唤醒所有在等待池中的线程,之后他们都可以回到锁池竞争该对象的锁。

参考资料

Java Thread的join() 之刨根问底

java中的notify和notifyAll有什么区别? - 大王叫我来巡山的回答 - 知乎