多线程等待所有子线程执行完使用总结(1)——wait()和notify(),join()方法

时间:2023-02-15 20:59:58

多线程等待所有子线程执行完使用总结(1)——wait()和notify(),join()方法

问题背景

我们在日常开发和学习过程中,经常会使用到多线程的场景,其中我们经常会碰到,我们代码需要等待某个或者多个线程执行完再开始执行,那么这种场景可以有多少方法实现呢?本文就对这个场景的解决方案进行初步的介绍。

问题分析

1、Object的wait()和notify()方法

等待(wait):一个线程因为执行某个操作所需的保护条件未满足而被暂停的过程。 通知(notify):一个线程更新了共享变量,使得其他线程需要的保护条件成立,唤醒了被暂停的线程的过程。 wait()方法的执行线程叫等待线程,notify()方法执行的线程叫通知线程。 demo代码如下:

package composer

import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import java.lang.Thread.sleep

class TestWaitActivity : AppCompatActivity() {
    private val lockObject = Object()

    companion object {
        const val TAG = "TestWaitActivity"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_test_wait)

        Log.d(TAG, "onCreate begin")

        val threadWait = WaitThread()
        threadWait.start()

        // 获取当前系统时间
        val beginTime = System.currentTimeMillis()

        synchronized(lockObject) {
            try {
                Log.d(TAG, "主线程开始等待")
                lockObject.wait()
            } catch (e: InterruptedException) {
                e.printStackTrace()
            }
        }
        Log.d(TAG, "耗时:" + (System.currentTimeMillis() - beginTime))
    }

    inner class WaitThread: Thread() {
        override fun run() {
            Log.d(TAG, "WaitThread run begin")
            synchronized (lockObject) {
                try {
                    Log.d(TAG, "WaitThread synchronized")
                    // 模拟耗时任务
                    sleep(3000)
                    lockObject.notify()

                } catch (e: InterruptedException) {
                    e.printStackTrace();
                }

            }
        }
    }
}

运行结果如下: 多线程等待所有子线程执行完使用总结(1)——wait()和notify(),join()方法 运行结果分析: wait()方法可以使当前线程A马上失去对象锁并且沉睡,直到对象调用notify()唤醒该线程。此时持有对象锁的线程会先行执行完毕,然后再将对象锁交给沉睡的线程继续执行。

2、线程的join()方法

thread.join()把指定的线程加入到当前线程,可以将两个交替执行的线程合并为顺序执行的线程。比如在线程B中调用了线程A的Join()方法,那么要等线程A执行完毕后,才会继续执行线程B。demo代码如下:

fun main() {
    val thread1 = Thread {
        run {
            println("i am thread1")
            // 模拟耗时任务
            Thread.sleep(2000)
            println("i am thread1 sleep ok")
        }
    }

    val thread2 = Thread {
        run {
            thread1.join()
            println("i am thread2")
        }
    }
    thread1.start()

    thread2.start()
}

运行结果如下: 多线程等待所有子线程执行完使用总结(1)——wait()和notify(),join()方法 运行结果分析: 线程2的run()方法中调用thread1.join(),这个位置会等thread1执行完才会继续去执行线程2中的代码。 这里也可以看下Thread类join的源码也比较容易理解: (1)java.lang.Thread#join()

    /**
     * Waits for this thread to die.
     *
     * <p> An invocation of this method behaves in exactly the same
     * way as the invocation
     *
     * <blockquote>
     * {@linkplain #join(long) join}{@code (0)}
     * </blockquote>
     *
     * @throws  InterruptedException
     *          if any thread has interrupted the current thread. The
     *          <i>interrupted status</i> of the current thread is
     *          cleared when this exception is thrown.
     */
    public final void join() throws InterruptedException {
        join(0);
    }

(2)java.lang.Thread#join(long)

    public final void join(long millis)
    throws InterruptedException {
        synchronized(lock) {
        long base = System.currentTimeMillis();
        long now = 0;

        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }

        if (millis == 0) {
            while (isAlive()) {
                lock.wait(0);
            }
        } else {
            while (isAlive()) {
                long delay = millis - now;
                if (delay <= 0) {
                    break;
                }
                // 通过Object对象的wait方法,进行一个延迟时间的等待
                lock.wait(delay);
                now = System.currentTimeMillis() - base;
            }
        }
        }
    }

问题总结

本文主要介绍了等待线程结束再执行的两个方案,(1)Object的wait()和notify()方法,(2)线程的join()方法,后面会继续介绍CountDownLatch类、CyclicBarrier类和自定义个一个原子类型计数器的方法,有兴趣的同学可以进一步深入研究。