多线程---java判断多线程结束




需求应用场景

我们多线程抓取数据或者多线程执行任务时,有时候需要多线程执行完后      才能执行其他的任务。

这种情况下  我们就需要判断多线程什么时候执行完。有以下几种方法(推荐方法二)





方法一 isTerminated

使用isTerminated来判断所有任务都已完成,会返回true。但是前提是启用shutdown顺序关闭。

shutdown

void shutdown()
启动一次顺序关闭,执行以前提交的任务,但不接受新任务。如果已经关闭,则调用没有其他作用。
抛出:
SecurityException - 如果安全管理器存在并且关闭,此 ExecutorService 可能操作某些不允许调用者修改的线程(因为它没有保持RuntimePermission("modifyThread")),或者安全管理器的 checkAccess 方法拒绝访问。


isTerminated

boolean isTerminated()
如果关闭后所有任务都已完成,则返回 true。注意,除非首先调用 shutdown 或 shutdownNow,否则 isTerminated 永不为 true。
返回:
如果关闭后所有任务都已完成,则返回 true





使用例子

先调用shundown再调用isTerminated判断

/**
 * 采用线程池开启多个子线程,主线程等待所有的子线程执行完毕  
 */  
public static void moreThread() {  
    try {  
        int threadNum = 0;  
        for (int i = 0; i < 10; i++) {  
            threadNum++;  
              
            final int currentThreadNum = threadNum;  
            exe.execute(new Runnable() {  
                  
                @Override  
                public void run() {  
                    try {  
                        System.out.println("子线程[" + currentThreadNum + "]开启");  
                        Thread.sleep(1000*10);  
                    } catch (InterruptedException e) {  
                        e.printStackTrace();  
                    }finally{  
                        System.out.println("子线程[" + currentThreadNum + "]结束");  
                    }  
                }  
            });    
        }  
          
        System.out.println("已经开启所有的子线程");  
        exe.shutdown();  
        System.out.println("shutdown():启动一次顺序关闭,执行以前提交的任务,但不接受新任务。");  
        while(true){  
            if(exe.isTerminated()){  
                System.out.println("所有的子线程都结束了!");  
                break;  
            }  
            Thread.sleep(1000);    
        }  
          
    } catch (InterruptedException e) {  
        e.printStackTrace();  
    }finally{  
        System.out.println("主线程结束");  
    }  
}  



输出结果

子线程[1]开启  
子线程[6]开启  
子线程[2]开启  
子线程[5]开启  
子线程[9]开启  
已经开启所有的子线程  
子线程[3]开启  
子线程[7]开启  
子线程[10]开启  
shutdown():启动一次顺序关闭,执行以前提交的任务,但不接受新任务。  
子线程[4]开启  
子线程[8]开启  
子线程[6]结束  
子线程[3]结束  
子线程[7]结束  
子线程[2]结束  
子线程[1]结束  
子线程[5]结束  
子线程[9]结束  
子线程[4]结束  
子线程[10]结束  
子线程[8]结束  
所有的子线程都结束了!  
主线程结束







方法二CountDownLatch

CountDownLatch是一个同步辅助类,用来判断多线程是否执行完成。

它的原理是首先声明一共运行多少个线程任务,然后每完成一个线程任务,计数器就减一,当计数器等于0,说明所有的多线程都运行完成了。


主要方法

 public CountDownLatch(int count);
 public void countDown();
 public void await() throws InterruptedException
 
构造方法参数指定了计数的次数
countDown方法,当前线程调用此方法,则计数减一
await方法,调用此方法会一直阻塞当前线程,直到计时器的值为0。


使用方法

如果你确定了你开启的线程数。 
那么在主程序运行前。 
CountDownLatch countdown = new CountDownLatch(10);//这里的10就是你的子线程数。 

在每个子线程结束后,调用 countdown.countDown(); 

在主线程里启动子线程的方法后面添加。 
countdown.await();//这里进行同步等待 

等所有子线程结束后,执行 countdown.await()后面的代码。



使用例子

public class CountDownLatchDemo {  
    final static SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");  
    public static void main(String[] args) throws InterruptedException {  
        CountDownLatch latch=new CountDownLatch(2);//两个工人的协作  
        Worker worker1=new Worker("zhang san", 5000, latch);  
        Worker worker2=new Worker("li si", 8000, latch);  
        worker1.start();//  
        worker2.start();//  
        latch.await();//等待所有工人完成工作  
        System.out.println("all work done at "+sdf.format(new Date()));  
    }  
      
      
    static class Worker extends Thread{  
        String workerName;   
        int workTime;  
        CountDownLatch latch;  
        public Worker(String workerName ,int workTime ,CountDownLatch latch){  
             this.workerName=workerName;  
             this.workTime=workTime;  
             this.latch=latch;  
        }  
        public void run(){  
            System.out.println("Worker "+workerName+" do work begin at "+sdf.format(new Date()));  
            doWork();//工作了  
            System.out.println("Worker "+workerName+" do work complete at "+sdf.format(new Date()));  
            latch.countDown();//工人完成工作,计数器减一  
  
        }  
          
        private void doWork(){  
            try {  
                Thread.sleep(workTime);  
            } catch (InterruptedException e) {  
                e.printStackTrace();  
            }  
        }  
    }  
      
       
}  
 


输出结果

Worker zhang san do work begin at 2011-04-14 11:05:11
Worker li si do work begin at 2011-04-14 11:05:11
Worker zhang san do work complete at 2011-04-14 11:05:16
Worker li si do work complete at 2011-04-14 11:05:19
all work done at 2011-04-14 11:05:19







方法三CyclicBarrier

CyclicBarrier ,一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。CyclicBarrier 支持一个可选的 Runnable 命令,在一组线程中的最后一个线程到达之后(但在释放所有线程之前),该命令只在每个屏障点运行一次。若在继续所有参与线程之前更新共享状态,此屏障操作 很有用。 


主要方法

//设置parties、count及barrierCommand属性。
CyclicBarrier(int):

//当await的数量到达了设定的数量后,首先执行该Runnable对象。
CyclicBarrier(int,Runnable):

//通知barrier已完成线程
await():







使用例子

我们需要统计全国的业务数据。其中各省的数据库是独立的,也就是说按省分库。并且统计的数据量很大,统计过程也比较慢。为了提高性能,快速计算。我们采取并发的方式,多个线程同时计算各省数据,最后再汇总统计。在这里CyclicBarrier就非常有用。看代码: 
主要类:
/**  
 * 各省数据独立,分库存偖。为了提高计算性能,统计时采用每个省开一个线程先计算单省结果,最后汇总。  
 *   
 * @author guangbo email:weigbo@163.com  
 *   
 */  
public class Total {     
    // private ConcurrentHashMap result = new ConcurrentHashMap();   
  
    public static void main(String[] args) {   
        TotalService totalService = new TotalServiceImpl();   
        CyclicBarrier barrier = new CyclicBarrier(5,   
                new TotalTask(totalService));   
  
        // 实际系统是查出所有省编码code的列表,然后循环,每个code生成一个线程。   
        new BillTask(new BillServiceImpl(), barrier, "北京").start();   
        new BillTask(new BillServiceImpl(), barrier, "上海").start();   
        new BillTask(new BillServiceImpl(), barrier, "广西").start();   
        new BillTask(new BillServiceImpl(), barrier, "四川").start();   
        new BillTask(new BillServiceImpl(), barrier, "黑龙江").start();   
  
    }   
}   
  
/**  
 * 主任务:汇总任务  
 */  
class TotalTask implements Runnable {   
    private TotalService totalService;   
  
    TotalTask(TotalService totalService) {   
        this.totalService = totalService;   
    }   
  
    public void run() {   
        // 读取内存中各省的数据汇总,过程略。   
        totalService.count();   
        System.out.println("=======================================");   
        System.out.println("开始全国汇总");   
    }   
}   
  
/**  
 * 子任务:计费任务  
 */  
class BillTask extends Thread {   
    // 计费服务   
    private BillService billService;   
    private CyclicBarrier barrier;   
    // 代码,按省代码分类,各省数据库独立。   
    private String code;   
  
    BillTask(BillService billService, CyclicBarrier barrier, String code) {   
        this.billService = billService;   
        this.barrier = barrier;   
        this.code = code;   
    }   
  
    public void run() {   
        System.out.println("开始计算--" + code + "省--数据!");   
        billService.bill(code);   
        // 把bill方法结果存入内存,如ConcurrentHashMap,vector等,代码略   
        System.out.println(code + "省已经计算完成,并通知汇总Service!");   
        try {   
            // 通知barrier已经完成   
            barrier.await();   
        } catch (InterruptedException e) {   
            e.printStackTrace();   
        } catch (BrokenBarrierException e) {   
            e.printStackTrace();   
        }   
    }   
  
} 

 

输出结果

开始计算--北京省--数据! 
开始计算--上海省--数据! 
北京省已经计算完成,并通知汇总Service! 
开始计算--四川省--数据! 
四川省已经计算完成,并通知汇总Service! 
上海省已经计算完成,并通知汇总Service! 
开始计算--广西省--数据! 
广西省已经计算完成,并通知汇总Service! 
开始计算--黑龙江省--数据! 
黑龙江省已经计算完成,并通知汇总Service! 



评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张小凡vip

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值