文档章节

多线程001 - 主线程等待子线程结束

闪电
 闪电
发布于 2016/07/24 22:35
字数 1830
阅读 10
收藏 0

在很多时候,我们期望实现这么一种功能:在主线程中启动一些子线程,等待所有子线程执行结束后,主线程再继续执行。比如:老板分配任务,众多工人开始工作,等所有工人完成工作后,老板进行检查。

解决方法分析:

  1. 主线程通过join等待所有子线程完成后,继续执行;
  2. 主线程知道子线程的数量、未完成子线程数量,主线程等待所有子线程完成后,才继续执行。

通过join实现

第一种方式,可以直接调用Java API中关于线程的join方法等待该线程终止,可以直接实现。

每个工人都是一个线程,其run方法是工作的实现,每个工人通过名字进行区分,具体代码如下:

package howe.demo.thread.join;


import java.util.Random;
import java.util.concurrent.TimeUnit;


/**
 * @author liuxinghao
 * @version 1.0 Created on 2014年9月12日
 */
public class Worker extends Thread {
    private String workerName;


    public Worker(String workerName) {
        this.workerName = workerName;
    }


    /**
     * @see java.lang.Thread#run()
     */
    @Override
    public void run() {
        System.out.println(this.workerName + "正在干活...");
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException e) {
        }
        System.out.println(this.workerName + "活干完了!");
    }


    public String getWorkerName() {
        return workerName;
    }
}

老板招收工人,然后安排工人工作,之后等待工人工作完,检查工人的工作成果(资本家啊。。。),具体代码如下:

package howe.demo.thread.join;


import java.util.List;


/**
 * @author liuxinghao
 * @version 1.0 Created on 2014年9月12日
 */
public class Boss {
    private List<Worker> workers;


    public Boss(List<Worker> workers) {
        System.out.println("老板招收工人。");
        this.workers = workers;
    }


    public void work() {
        System.out.println("老板开始安排工人工作...");
        for (Worker worker : workers) {
            System.out.println("老板安排" + worker.getWorkerName() + "的工作");
            worker.start();
        }
        System.out.println("老板安排工作结束...");


        System.out.println("老板正在等所有的工人干完活......");
        for (Worker w : workers) {
            try {
                w.join();
            } catch (InterruptedException e) {
            }
        }
        System.out.println("工人活都干完了,老板开始检查了!");
    }
}

现在写main方法进行测试:

package howe.demo.thread.join;


import java.util.ArrayList;
import java.util.List;


/**
 * @author liuxinghao
 * @version 1.0 Created on 2014年9月12日
 */
public class JoinDemo {
    public static void main(String[] args) {
        Worker w1 = new Worker("张三");
        Worker w2 = new Worker("李四");
        Worker w3 = new Worker("王五");


        List<Worker> workers = new ArrayList<Worker>();
        workers.add(w1);
        workers.add(w2);
        workers.add(w3);


        Boss boss = new Boss(workers);
        boss.work();


        System.out.println("main方法结束");
    }
}

执行结果为:

老板招收工人。
老板开始安排工人工作...
老板安排张三的工作
老板安排李四的工作
张三正在干活...
老板安排王五的工作
李四正在干活...
老板安排工作结束...
老板正在等所有的工人干完活......
王五正在干活...
王五活干完了!
张三活干完了!
李四活干完了!
工人活都干完了,老板开始检查了!
main方法结束

通过计数器实现

第二种方式可以自己实现一种计数器,用于统计子线程总数、未完成线程数,当未完成线程数大约0,主线程等待;当未完成线程数等于0,主线程继续执行。

当然,既然我们现在想到这种方式,Java API的团队当然也会想到,JDK 1.5提供了CountDownLatch用于实现上述方法。

于是对上述的工人方法进行修改:

package howe.demo.thread.cdl;


import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;


/**
 * @author liuxinghao
 * @version 1.0 Created on 2014-9-12
 */
public class Worker extends Thread {
    private CountDownLatch downLatch;
    private String workerName;


    public Worker(CountDownLatch downLatch, String workerName) {
        this.downLatch = downLatch;
        this.workerName = workerName;
    }


    public void run() {
        System.out.println(this.workerName + "正在干活...");
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException ie) {
        }
        System.out.println(this.workerName + "活干完了!");
        this.downLatch.countDown();
    }


    public String getWorkerName() {
        return workerName;
    }
}

latch.countDown(),是用于在子线程执行结束后计数器减一,即未完成子线程数减一。

老板类也得做出相应的修改:

package howe.demo.thread.cdl;


import java.util.List;
import java.util.concurrent.CountDownLatch;


/**
 * @author liuxinghao
 * @version 1.0 Created on 2014-9-12
 */
public class Boss {
    private List<Worker> workers;
    private CountDownLatch downLatch;


    public Boss(List<Worker> workers, CountDownLatch downLatch) {
        this.workers = workers;
        this.downLatch = downLatch;
    }


    public void work() {
        System.out.println("老板开始安排工人工作...");
        for (Worker worker : workers) {
            System.out.println("老板安排" + worker.getWorkerName() + "的工作");
            worker.start();
        }
        System.out.println("老板安排工作结束...");


        System.out.println("老板正在等所有的工人干完活......");
        try {
            this.downLatch.await();
        } catch (InterruptedException e) {
        }
        System.out.println("工人活都干完了,老板开始检查了!");
    }


}

latch.await(),是等待子线程结束。

编写main方法进行验证:

package howe.demo.thread.cdl;


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;


/**
 * @author liuxinghao
 * @version 1.0 Created on 2014年9月15日
 */
public class CountDownLatchDemo {
    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(3);


        Worker w1 = new Worker(latch, "张三");
        Worker w2 = new Worker(latch, "李四");
        Worker w3 = new Worker(latch, "王五");


        List<Worker> workers = new ArrayList<Worker>();
        workers.add(w1);
        workers.add(w2);
        workers.add(w3);


        Boss boss = new Boss(workers, latch);


        boss.work();


        System.out.println("main方法结束");
    }
}

执行结果为:

老板开始安排工人工作...
老板安排张三的工作
老板安排李四的工作
张三正在干活...
老板安排王五的工作
李四正在干活...
老板安排工作结束...
老板正在等所有的工人干完活......
王五正在干活...
王五活干完了!
李四活干完了!
张三活干完了!
工人活都干完了,老板开始检查了!
main方法结束

使用循环栅栏CyclicBarrier实现

还有一种实现,这种方式不会阻塞主线程,但是会监听所有子线程结束。此处在上述的工人老板的场景中使用的话,代码如下:

工人类:

package howe.demo.thread.cyclicbarrier;


import java.util.Random;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.TimeUnit;


/**
 * @author liuxinghao
 * @version 1.0 Created on 2014年9月12日
 */
public class Worker extends Thread {
    private String workerName;
    private CyclicBarrier barrier;


    public Worker(String workerName, CyclicBarrier barrier) {
        this.workerName = workerName;
        this.barrier = barrier;
    }


    @Override
    public void run() {
        System.out.println(this.workerName + "正在干活...");
        try {
            TimeUnit.SECONDS.sleep(new Random().nextInt(10));
        } catch (InterruptedException e) {
        }
        System.out.println(this.workerName + "活干完了!");


        try {
            barrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }


    public String getWorkerName() {
        return workerName;
    }
}

老板类:

package howe.demo.thread.cyclicbarrier;


import java.util.List;


/**
 * @author liuxinghao
 * @version 1.0 Created on 2014-9-12
 */
public class Boss {
    private List<Worker> workers;


    public Boss(List<Worker> workers) {
        this.workers = workers;
    }


    public void work() {
        System.out.println("老板开始安排工人工作...");
        for (Worker worker : workers) {
            System.out.println("老板安排" + worker.getWorkerName() + "的工作");
            worker.start();
        }
        System.out.println("老板安排工作结束...");


        System.out.println("老板正在等所有的工人干完活......");
    }


}

main方法测试:

package howe.demo.thread.cyclicbarrier;


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CyclicBarrier;


/**
 * @author liuxinghao
 * @version 1.0 Created on 2014年9月15日
 */
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {
            @Override
            public void run() {
                System.out.println("工人活都干完了,老板开始检查了!");
            }
        });


        Worker w1 = new Worker("张三", barrier);
        Worker w2 = new Worker("李四", barrier);
        Worker w3 = new Worker("王五", barrier);


        List<Worker> workers = new ArrayList<Worker>();
        workers.add(w1);
        workers.add(w2);
        workers.add(w3);


        Boss boss = new Boss(workers);
        boss.work();


        System.out.println("main方法结束");
    }
}

执行结果为:

老板开始安排工人工作...
老板安排张三的工作
老板安排李四的工作
张三正在干活...
老板安排王五的工作
李四正在干活...
老板安排工作结束...
老板正在等所有的工人干完活......
王五正在干活...
main方法结束
李四活干完了!
王五活干完了!
张三活干完了!
工人活都干完了,老板开始检查了!

通过结果分析可以很清楚的看出,boss对象的work方法执行结束后,main方法即开始执行。(此处“老板正在等所有的工人干完活......”打印之后是“王五正在干活...”,然后才是“main方法结束”,是因为对于cpu的抢占,甚至有一定的概率是“main方法结束”会在最后打印。)

假设有这么一个功能,我们需要向数据库批量写入一些记录,然后记录这个操作使用的时间,但是我们又不想影响其他操作(即不想阻塞主线程),这个时候CyclicBarrier就派上用场了。

本文转载自:http://blog.csdn.net/liuxinghao/article/details/39299263#t1

闪电
粉丝 74
博文 392
码字总数 6789
作品 0
海淀
技术主管
私信 提问
python的多线程中的join的作用

1 python 默认参数创建线程后,不管主线程是否执行完毕,都会等待子线程执行完毕才一起退出,有无join结果一样 例子如下: 2 如果创建线程,并且设置了daemon为true,即thread.setDaemon(True...

Forande
2018/08/22
0
0
Java多线程学习(八)

在大多数情况下,主线程创建并启动子线程,如果子线程中要进行大量的耗时计算,主线程往往会在子线程结束之前结束掉。若是主线程想要等待子线程执行完成之后再结束,则必须用到方法join(). 方...

kakayang2011
2016/03/09
35
0
python多线程(线程的合并与后台线程)

线程的合并 python的Thread类中还提供了join()方法,使得一个线程可以等待另一个线程执行结束后再继续运行。这个方法还可以设定一个timeout参数,避免无休止的等待。因为两个线程顺序完成,看...

DragonRiver2015
2013/12/07
0
0
Java编程的逻辑 -- 并发章 -- 线程的基本协作机制

线程的基本协作 线程的基本协作示例 总结 线程的基本协作 多线程间除了竞争访问同一资源外,也经常需要相互协作的去执行一些任务。而对于协作的基本机制用的最多的无疑是wait/notify。 协作的...

HikariCP
2018/06/22
0
0
Java并发编程原理与实战六:主线程等待子线程解决方案

本文将研究的是主线程等待所有子线程执行完成之后再继续往下执行的解决方案 public class TestThread extends Thread { } 首先是一个线程,它执行完成需要5秒。 1、主线程等待一个子线程 pu...

pony1223
2018/07/23
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Linux learn(一)

参考书:鸟哥Linux私房菜-第四版 4.3 Linux的在线求助man page与info page man man: manual(操作说明)的简写。作用是查看某个文件或者指令的文档,操作手册,q退出 eg: man date 上图中的DAT...

lazy~
11分钟前
0
0
微信,QQ这类IM app怎么做——谈谈Websocket

前言 关于我和WebSocket的缘:我从大二在计算机网络课上听老师讲过之后,第一次使用就到了毕业之后的第一份工作。直到最近换了工作,到了一家是含有IM社交聊天功能的app的时候,我觉得我现在...

tantexian
12分钟前
0
0
Dubbo 支持哪些序列化协议?

面试题 dubbo 支持哪些通信协议?支持哪些序列化协议?说一下 Hessian 的数据结构?PB 知道吗?为什么 PB 的效率是最高的? 面试官心理分析 上一个问题,说说 dubbo 的基本工作原理,那是你必...

李红欧巴
16分钟前
8
0
Hyperledger Fabric Node.js如何使用基于通道的事件服务

本教程说明了基于通道的事件的使用。这些事件与现有事件类似,但是特定于单个通道。在设置侦听器时,客户端处理基于通道的事件有一些新选项。从v1.1开始,基于通道的事件是Hyperledger Fabri...

geek12345
22分钟前
0
0
Java中print、printf、println的区别

printf主要是继承了C语言的printf的一些特性,可以进行格式化输出 print就是一般的标准输出,但是不换行 println和print基本没什么差别,就是最后会换行

hellation_
37分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部