`
langgufu
  • 浏览: 2310213 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java主线程如何等待子线程执行结束(转)

阅读更多
工作中往往会遇到异步去执行某段逻辑, 然后先处理其他事情, 处理完后再把那段逻辑的处理结果进行汇总的产景, 这时候就需要使用线程了.
一个线程启动之后, 是异步的去执行需要执行的内容的, 不会影响主线程的流程,  往往需要让主线程指定后, 等待子线程的完成. 这里有几种方式.
站在 主线程的角度, 我们可以分为主动式和被动式.
主动式指主线主动去检测某个标志位, 判断子线程是否已经完成. 被动式指主线程被动的等待子线程的结束, 很明显, 比较符合人们的胃口. 就是你事情做完了, 你告诉我, 我汇总一下, 哈哈.
那么主线程如何等待子线程工作完成呢. 很简单, Thread 类给我们提供了join 系列的方法, 这些方法的目的就是等待当前线程的die. 举个例子:
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
</div>
public class Threads {
 
public static void main(String[] args) {
SubThread thread = new SubThread();
thread.start();
//主线程处理其他工作,让子线程异步去执行.
mainThreadOtherWork();
System.out.println("now waiting sub thread done.");
//主线程其他工作完毕,等待子线程的结束, 调用join系列的方法即可(可以设置超时时间)
try {
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("now all done.");
}
 
private static void mainThreadOtherWork() {
System.out.println("main thread work start");
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread work done.");
}
 
public static class SubThread extends Thread{
@Override
public void run() {
working();
}
 
private void working() {
System.out.println("sub thread start working.");
busy();
System.out.println("sub thread stop working.");
}
 
private void busy() {
try {
sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
 
}
}
 
本程序的数据有可能是如下:
  1. main thread work start
  2. sub thread start working.
  3. main thread work done.
  4. now waiting sub thread done.
  5. sub thread stop working.
  6. now all done.
忽略标号, 当然输出也有可能是1和2调换位置了. 这个我们是无法控制的. 我们看下线程的join操作, 究竟干了什么.
1
2
3
public final void join() throws InterruptedException {
join(0);
}
这里是调用了
public final synchronized void join(long millis) 
方法, 参数为0, 表示没有超时时间, 等到线程结束为止. join(millis)方法里面有这么一段代码:
1
2
3
while (isAlive()) {
wait(0);
}
说明, 当线程处于活跃状态的时候, 会一直等待, 直到这里的isAlive方法返回false, 才会结束.isAlive方法是一个本地方法, 他的作用是判断线程是否已经执行结束. 注释是这么写的: 

Tests if this thread is alive. A thread is alive if it has been started and has not yet died.

 
可见, join系列方法可以帮助我们等待一个子线程的结束.
 
那么要问, 有没有另外一种方法可以等待子线程结束? 当然有的, 我们可以使用并发包下面的Future模式.
Future是一个任务执行的结果, 他是一个将来时, 即一个任务执行, 立即异步返回一个Future对象, 等到任务结束的时候, 会把值返回给这个future对象里面. 我们可以使用ExecutorService接口来提交一个线程.
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
public class Threads {
 
static ExecutorService executorService = Executors.newFixedThreadPool(1);
 
@SuppressWarnings("rawtypes")
public static void main(String[] args) throws InterruptedException, ExecutionException {
SubThread thread = new SubThread();
// thread.start();
Future future = executorService.submit(thread);
mainThreadOtherWork();
System.out.println("now waiting sub thread done.");
future.get();
// try {
// thread.join();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println("now all done.");
executorService.shutdown();
}
 
private static void mainThreadOtherWork() {
System.out.println("main thread work start");
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread work done.");
}
 
public static class SubThread extends Thread{
@Override
public void run() {
working();
}
 
private void working() {
System.out.println("sub thread start working.");
busy();
System.out.println("sub thread stop working.");
}
 
private void busy() {
try {
sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
 
}
 
}
 
这里, ThreadPoolExecutor 是实现了 ExecutorService的方法, sumbit的过程就是把一个Runnable接口对象包装成一个 Callable接口对象, 然后放到 workQueue里等待调度执行. 当然, 执行的启动也是调用了thread的start来做到的, 只不过这里被包装掉了. 另外, 这里的thread是会被重复利用的, 所以这里要退出主线程, 需要执行以下shutdown方法以示退出使用线程池. 扯远了. 
 
这种方法是得益于Callable接口和Future模式, 调用future接口的get方法, 会同步等待该future执行结束, 然后获取到结果. Callbale接口的接口方法是 V call(); 是可以有返回结果的, 而Runnable的 void run(), 是没有返回结果的. 所以, 这里即使被包装成Callbale接口, future.get返回的结果也是null的.如果需要得到返回结果, 建议使用Callable接口.
 
通过队列来控制线程的进度, 是很好的一个理念. 我们完全可以自己搞个队列, 自己控制. 这样也可以实现. 不信看代码:
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
public class Threads {
 
// static ExecutorService executorService = Executors.newFixedThreadPool(1);
static final BlockingQueue queue = new ArrayBlockingQueue(1);
public static void main(String[] args) throws InterruptedException, ExecutionException {
SubThread thread = new SubThread(queue);
thread.start();
// Future future = executorService.submit(thread);
mainThreadOtherWork();
System.out.println("now waiting sub thread done.");
// future.get();
queue.take();
// try {
// thread.join();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println("now all done.");
// executorService.shutdown();
}
 
private static void mainThreadOtherWork() {
System.out.println("main thread work start");
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread work done.");
}
 
public static class SubThread extends Thread{
 
private BlockingQueue queue;
 
/**
* @param queue
*/
public SubThread(BlockingQueue queue) {
this.queue = queue;
}
 
@Override
public void run() {
try{
working();
}finally{
try {
queue.put(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
 
}
 
private void working() {
System.out.println("sub thread start working.");
busy();
System.out.println("sub thread stop working.");
}
 
private void busy() {
try {
sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
 
}
 
}
 
这里是得益于我们用了一个阻塞队列, 他的put操作和take操作都会阻塞(同步), 在满足条件的情况下.当我们调用take()方法时, 由于子线程还没结束, 队列是空的, 所以这里的take操作会阻塞, 直到子线程结束的时候, 往队列里面put了个元素, 表明自己结束了. 这时候主线程的take()就会返回他拿到的数据. 当然, 他拿到什么我们是不必去关心的.
以上几种情况都是针对子线程只有1个的时候. 当子线程有多个的时候, 情况就不妙了.
第一种方法, 你要调用很多个线程的join, 特别是当你的线程不是for循环创建的, 而是一个一个创建的时候.
第二种方法, 要调用很多的future的get方法, 同第一种方法.
第三种方法, 比较方便一些, 只需要每个线程都在queue里面 put一个元素就好了.但是, 第三种方法, 这个队列里的对象, 对我们是毫无用处, 我们为了使用队列, 而要不明不白浪费一些内存,
那有没有更好的办法呢?有的, concurrency包里面提供了好多有用的东东, 其中, CountDownLanch就是我们要用的.
CountDownLanch 是一个倒数计数器, 给一个初始值(>=0), 然后每countDown一次就会减1, 这很符合等待多个子线程结束的场景: 一个线程结束的时候, countDown一次, 直到所有都countDown了 , 那么所有子线程就都结束了.
先看看CountDownLanch有哪些方法:
await: 会阻塞等待计数器减少到0位置. 带参数的await是多了等待时间.
countDown: 将当前的技术减1
getCount(): 返回当前的计数
显而易见, 我们只需要在子线程执行之前, 赋予初始化countDownLanch, 并赋予线程数量为初始值.
每个线程执行完毕的时候, 就countDown一下.主线程只需要调用await方法, 可以等待所有子线程执行结束, 看代码:
 
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
public class Threads {
 
// static ExecutorService executorService = Executors.newFixedThreadPool(1);
static final BlockingQueue queue = new ArrayBlockingQueue(1);
public static void main(String[] args) throws InterruptedException, ExecutionException {
int threads = 5;
CountDownLatch countDownLatch = new CountDownLatch(threads);
for(int i=0;i<threads;i++){
SubThread thread = new SubThread(2000*(i+1), countDownLatch);
thread.start();
}
// Future future = executorService.submit(thread);
mainThreadOtherWork();
System.out.println("now waiting sub thread done.");
// future.get();
// queue.take();
countDownLatch.await();
// try {
// thread.join();
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
System.out.println("now all done.");
// executorService.shutdown();
}
 
private static void mainThreadOtherWork() {
System.out.println("main thread work start");
try {
Thread.sleep(3000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main thread work done.");
}
 
public static class SubThread extends Thread{
 
// private BlockingQueue queue;
private CountDownLatch countDownLatch;
private long work;
 
/**
* @param queue
*/
// public SubThread(BlockingQueue queue) {
// this.queue = queue;
// this.work = 5000L;
// }
 
public SubThread(long work, CountDownLatch countDownLatch) {
// this.queue = queue;
this.countDownLatch = countDownLatch;
this.work = work;
}
 
@Override
public void run() {
try{
working();
}finally{
// try {
// queue.put(1);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
countDownLatch.countDown();
}
 
}
 
private void working() {
System.out.println(getName()+" sub thread start working.");
busy();
System.out.println(getName()+" sub thread stop working.");
}
 
private void busy() {
try {
sleep(work);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
 
}
}
此种方法也适用于使用 ExecutorService summit 的任务的执行.
另外还有一个并发包的类CyclicBarrier, 这个是(子)线程之间的互相等待的利器. 栅栏, 就是把大家都在一个地方堵住, 就像水闸, 等大家都完成了之前的操作, 在一起继续下面的操作.
不过就不再本篇的讨论范围内了.
EO
分享到:
评论

相关推荐

    JAVA主线程等待子线程执行完毕再执行[参照].pdf

    JAVA 主线程等待子线程执行完毕再执行 JAVA 中的线程控制是非常重要的一部分,而在实际开发中,我们经常会遇到需要主线程等待子线程执行完毕再执行的情况。这种情况下,我们可以使用两种方式来实现:主动式和被动式...

    Java主线程等待子线程执行完毕

    Java 主线程等待子线程执行完毕 Java 中的多线程编程是非常重要的一部分,特别是在需要并发执行多个任务的情况下。然而,在某些情况下,我们需要等待所有子线程执行完毕后再继续执行主线程的剩余动作。这时,我们...

    Java简单实现“主线程等待所有子线程完成再继续”

    一旦调用了`join()`,主线程会被阻塞,直到被调用的线程执行完毕。例如: ```java Thread childThread = new Thread(() -&gt; { // 子线程的代码 }); childThread.start(); try { childThread.join(); // 主线程在...

    主线程等待子多线程(无结果返回)执行完成再继续执行

    在Java中,主线程可以通过调用子线程的`join()`方法来等待该子线程结束。`join()`会阻塞当前线程(即主线程),直到被调用的子线程执行完毕。例如: ```java Thread childThread = new Thread(() -&gt; { // 子线程...

    Java多线程--等待所有子线程执行完的五种方法.docx

    例如,如果有多个子线程,我们可以在每个子线程执行`join()`,这样主线程会依次等待每个子线程完成。例如: ```java Thread thread1 = new Thread(...); Thread thread2 = new Thread(...); thread1.start(); ...

    Java多线程--让主线程等待所有子线程执行完毕

    然而,在多线程环境下,如果直接在主线程中记录开始和结束时间,由于子线程是并发执行的,主线程不会等待子线程完成就会继续执行,导致记录的总用时可能并不准确。 **2. 使用`join()`方法的局限性** 尝试在每个子...

    Python多线程:主线程等待所有子线程结束代码

    本篇文章将深入探讨如何在Python中实现多线程,并让主线程等待所有子线程结束。 首先,我们需要了解Python中的`threading`模块,它是Python标准库中用于处理多线程的模块。在示例代码中,我们创建了两个函数`a()`和...

    多线程执行完后主程序再执行(包括需要子线程返回结果)

    首先,多线程执行时,主线程会创建并启动子线程,然后继续执行自己的代码,这可能导致子线程尚未完成时,主线程已经结束,从而造成数据不一致或资源未释放的问题。为解决这个问题,我们需要使用同步机制,如Java中的...

    Java多线程–让主线程等待所有子线程执行完毕

    线程启动后,它们会并发运行,而主线程也会继续执行,导致主线程在for循环结束后立即计算总耗时,此时所有子线程可能并未全部完成。 要解决“让主线程等待所有子线程执行完毕”的问题,可以采用以下策略: 1. 使用...

    Java多线程--让主线程等待所有子线程执行完毕在执行

    在Java多线程编程中,有时我们需要确保主线程在所有子线程完成执行后再继续执行。这在处理大量并发任务,比如数据导入、并行计算等场景中是常见的需求。以下是如何实现这一功能的详细解释。 首先,让我们理解为什么...

    Java实现等待所有子线程结束后再执行一段代码的方法

    //主线程等待子线程执行输出 System.out.println(count); } } ``` 在上面的代码中,我们使用 CountDownLatch 来等待 1000 个子线程完成任务后再继续执行主线程。在 inc() 方法中,我们使用 synchronized 关键字...

    子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次,请写出程序。

    # 确保子线程执行一次后再执行主线程 child.join() for _ in range(50): main_thread() child_thread() ``` 在Java中,可以使用`Thread`类或`ExecutorService`来实现相同的功能: ```java public class Main { ...

    Java多线程机制和线程之间的协作

    当创建Thread的子类时,你需要重写run()方法,这个方法定义了线程执行的具体逻辑。例如,`Lefthand`和`Righthand`类就是两个Thread的子类,分别重写了run()方法,用来打印"我是左手线程"和"我是右手线程"。在main...

    Java多线程机制(讲述java里面与多线程有关的函数)

    每个Java程序都有一个主线程,即由JVM启动并执行main方法的线程。线程代表了程序中的执行流,可以在不同的线程之间切换以共享CPU时间。线程的状态包括新建、运行、中断和死亡。线程的生命周期始于新建,通过调用...

    java使用CountDownLatch等待多线程全部执行完成

    CountDownLatch 的 await 方法还支持超时时间的设置,当等待超时时间子线程还没执行完将不再等待继续执行主线程。例如,public boolean await(long timeout, TimeUnit unit)。 使用 CountDownLatch 可以大大提高...

    Java创建子线程的两种方法

    // 线程执行结束 System.out.println("执行完成! " + getName()); } } public class HelloWorld { public static void main(String[] args) { // 创建线程 t1 Thread t1 = new MyThread(); // 开始线程 t1 ...

    Java线程之join_动力节点Java学院整理

    当`millis`参数不为0时,`join()`会设置一个超时时间,如果子线程在指定时间内没有完成,主线程将结束等待并继续执行。 在实际应用中,`join()`常常用于确保某些关键操作在多线程环境中按顺序执行。例如,在并发...

    Android线程结束——合理的结束你想结束的线程

    Thread是Java提供的基础线程类,可以自定义线程执行逻辑;而AsyncTask是Android提供的轻量级异步任务框架,适合短时间、快速的任务,且能方便地与UI进行交互。 对于Thread的结束,有以下几点需要注意: 1. **不要...

    Java并发编程示例(六):等待线程执行终止

    为了等待这两个线程执行完成,我们调用了`thread1.join()`和`thread2.join()`,这使得主线程会阻塞,直到`thread1`和`thread2`都完成它们的工作。加入`try-catch`块来捕获可能抛出的`InterruptedException`,这是...

    java 携子之手 与子偕老(join)

    start()方法用于启动新线程,而join()方法则用于确保当前线程等待该线程执行结束。例如: ```java Thread thread = new Thread(new Runnable() { @Override public void run() { // 新线程的代码 } }); thread....

Global site tag (gtag.js) - Google Analytics