多线程等待所有子线程执行完使用总结(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();
}
}
}
}
}
运行结果如下: 运行结果分析: 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()
}
运行结果如下: 运行结果分析: 线程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类和自定义个一个原子类型计数器的方法,有兴趣的同学可以进一步深入研究。