join方法与countDownLatch与CyclicBarrier的区别

来源:互联网 发布:q币可以在淘宝买东西吗 编辑:程序博客网 时间:2024/06/06 19:54

join方法:

当前线程中调用thread.join()会导致当前线程阻塞,此时只有当thread线程执行完后,当前线程才能继续往下执行。
join的工作原理是,不停检查thread是否存活,如果存活则让当前线程永远wait,直到thread线程终止
可以看看join的源码:

 public final synchronized void join(long l)        throws InterruptedException    {        long l1 = System.currentTimeMillis();        long l2 = 0L;        if(l < 0L)            throw new IllegalArgumentException("timeout value is negative");        if(l == 0L)//当线程是存活状态,则会一直等待下去            for(; isAlive(); wait(0L));        else            do            {                if(!isAlive())                    break;                long l3 = l - l2;                if(l3 <= 0L)                    break;                wait(l3);                l2 = System.currentTimeMillis() - l1;            } while(true);    }

我们可以看看具体例子:

  public class JoinTest extends Thread {      //工作者名      private String name;      //工作时间      private long time;      public JoinTest(String name, long time) {          this.name = name;          this.time = time;      }      @Override      public void run() {          // TODO 自动生成的方法存根          try {              System.out.println(name+"开始工作");              Thread.sleep(time);              System.out.println(name+"工作完成,耗费时间="+time);          } catch (InterruptedException e) {              // TODO 自动生成的 catch 块              e.printStackTrace();          }         }  }  public static void main(String[] args) throws ParseException, InterruptedException{    JoinTest worker0 = new JoinTest("worker0", 5000);      JoinTest worker1 = new JoinTest("worker1", 5000);      JoinTest worker2 = new JoinTest("worker2", 5000);      worker0.start();      worker1.start();      worker0.join();      worker1.join();      System.out.println("准备工作就绪");      worker2.start();      

}
控制台输出顺序如下:
这里写图片描述

观察控制台,可以明确看出,只有当线程worker0,worker1执行完毕之后,worker2才会开始执行。

countDownLatch:

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

**构造方法摘要**CountDownLatch(int count) 构造一个用给定计数初始化的 CountDownLatch。

方法摘要

  1. void | await()
    使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
  2. boolean | await(long timeout, TimeUnit unit)
    使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断或超出了指定的等待时间。
  3. void | countDown()
    递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
  4. long | getCount()
    返回当前计数。
  5. String | toString()
    返回标识此锁存器及其状态的字符串。

CountDownLatch 的一个有用特性是,它不要求调用 countDown 方法的线程等到计数到达零时才继续,而在所有线程都能通过之前,它只是阻止任何线程继续通过一个 await。
countDown 递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
如果当前计数大于零,则将计数减少。如果新的计数为零,出于线程调度目的,将重新启用所有的等待线程。
如果当前计数等于零,则不发生任何操作。
如上面join的例子,将之改成为CountDownLatch

   public class JoinTest extends Thread {          //工作者名          private String name;          //工作时间          private long time;          CountDownLatch count;        public JoinTest(String name, long time,CountDownLatch count) {              this.name = name;              this.time = time;              this.count = count;        }          @Override          public void run() {              System.out.println(name+"开始工作");              count.countDown();            System.out.println(name+"工作完成,耗费时间="+time);             }      }    public static void main(String[] args) throws ParseException, InterruptedException    {        CountDownLatch count = new CountDownLatch(2);        JoinTest worker0 = new JoinTest("worker0", 5000,count);          JoinTest worker1 = new JoinTest("worker1", 5000,count);          JoinTest worker2 = new JoinTest("worker2", 5000,count);          worker0.start();          worker1.start();          count.await();        System.out.println("准备工作就绪");          worker2.start();   }

这里写图片描述

从控制台结果可以看出,执行效果还是一样的。count计数器为零之前,当前线程会一直在等待着。
项目中countDown可运用大批量数据处理,例如处理100万的数据,可以将100万拆分成100个线程放入线程池中,计数器为100,只有当这100万的数据全部处理完毕,线程往下执行,告之数据已经全部处理完毕

  int rum = 0;    CountDownLatch countDownLatch;    public CountDownLatchTest(CountDownLatch countDownLatch,int rum)    {        this.countDownLatch = countDownLatch;        this.rum = rum;    }    @Override    public void run()    {        // TODO Auto-generated method stub        super.run();        System.out.println("第"+rum+"批数据开始处理");        countDownLatch.countDown();    }    public static void main(String[] args) throws InterruptedException    {        // TODO Auto-generated method stub        //假设有100万的数据需要处理,分成10个线程处理        CountDownLatch count = new CountDownLatch(10);        for(int i =0 ;i<10;i++){            CountDownLatchTest a = new CountDownLatchTest(count,i);            a.start();        }        //处理完成之前所有线程一直等待着        count.await();        System.out.println("处理完毕");    }

这里写图片描述

CyclicBarrier:

类似于CountDownLatch,它也是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)。在涉及一组固定大小的线程的程序中,这些线程必须不时地互相等待,此时 CyclicBarrier 很有用。因为该 barrier 在释放等待线程后可以重用,所以称它为循环 的 barrier。

  • CyclicBarrier(int parties)
    创建一个新的CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,但它不会在启动 barrier 时执行预定义的操作。
  • CyclicBarrier(int parties, Runnable barrierAction)
    创建一个新的CyclicBarrier,它将在给定数量的参与者(线程)处于等待状态时启动,并在启动 barrier时执行给定的屏障操作,该操作由最后一个进入 barrier 的线程执行。

方法摘要
int | await()
在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
int | await(long timeout, TimeUnit unit)
在所有参与者都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。
int | getNumberWaiting()
返回当前在屏障处等待的参与者数目。
int | getParties()
返回要求启动此 barrier 的参与者数目。
boolean | isBroken()
查询此屏障是否处于损坏状态。
void | reset()
将屏障重置为其初始状态。

在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。
如果当前线程不是将到达的最后一个线程,出于调度目的,将禁用它,且在发生以下情况之一前,该线程将一直处于休眠状态:
● 最后一个线程到达;或者
● 其他某个线程中断当前线程;或者
● 其他某个线程中断另一个等待线程;或者
● 其他某个线程在等待 barrier 时超时;或者
● 其他某个线程在此 barrier 上调用 reset()。
如果当前线程:
● 在进入此方法时已经设置了该线程的中断状态;或者
● 在等待时被中断
则抛出 InterruptedException,并且清除当前线程的已中断状态。
如果在线程处于等待状态时 barrier 被 reset(),或者在调用 await 时 barrier 被损坏,抑或任意一个线程正处于等待状态,则抛出 BrokenBarrierException 异常。
如果任何线程在等待时被 中断,则其他所有等待线程都将抛出 BrokenBarrierException 异常,并将 barrier 置于损坏状态。
如果当前线程是最后一个将要到达的线程,并且构造方法中提供了一个非空的屏障操作,则在允许其他线程继续运行之前,当前线程将运行该操作。如果在执行屏障操作过程中发生异常,则该异常将传播到当前线程中,并将 barrier 置于损坏状态。

CyclicBarrier 处理方式可以看做并行,简单例子,比如赛跑,当所有运动员准备就绪时,裁判发枪了,所有人同时开始起跑,如果一个运动员还没准备好,那么裁判会让所有运动员会等他准备好之后再开始发枪。

public class CyclicBarrierTest{    public static void main(String[] args) throws IOException, InterruptedException {          //如果将参数改为4,但是下面只加入了3个选手,这永远等待下去          //Waits until all parties have invoked await on this barrier.           CyclicBarrier barrier = new CyclicBarrier(3);          ExecutorService executor = Executors.newFixedThreadPool(3);         executor.submit(new Thread(new Runner(barrier, "1号选手",5000)));          executor.submit(new Thread(new Runner(barrier, "2号选手",10000)));          executor.submit(new Thread(new Runner(barrier, "3号选手",3000)));          executor.shutdown();      }  }class Runner extends Thread {      // 一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point)      private CyclicBarrier barrier;      private String name;      private long time;      public Runner(CyclicBarrier barrier, String name, long time) {          super();          this.barrier = barrier;          this.name = name;        this.time = time;      }      @Override      public void run() {          try {              Thread.sleep(time);              System.out.println(name + " 准备好了...");              // barrier的await方法,在所有参与者都已经在此 barrier 上调用 await 方法之前,将一直等待。            System.out.println("在此之前已经有" +barrier.getNumberWaiting()+ "位选手 准备好了...");                barrier.await();          } catch (InterruptedException e) {              e.printStackTrace();          } catch (BrokenBarrierException e) {              e.printStackTrace();          }          System.out.println(name + " 裁判发枪,起跑!");      }  }  

这里写图片描述

0 0
原创粉丝点击