这几个类可以跨过线程进行应用
背景:我们操作线程的时候,有时候需要等待其他线程结束以后才能开始新的任务,这样线程之间的交互就需要一些工具类。
-
CountDownLatch(减减&等待)
作用:通过
CountDownLatch countDownLatch = new CountDownLatch(5);
设置需要等待的计数,通过countDown来进行计数改变,通过await进行阻塞等待直到为零开始下面的任务。代码:我们希望执行完子线程最后执行主线程。
public static void main(String[] args) { for (int i = 0; i < 5; i++) { final int num = i; new Thread(()->{ System.out.println("我是线程 "+ Thread.currentThread().getName()); },num+"").start(); } System.out.println(Thread.currentThread().getName()+" 线程都执行完了"); }
这样的结果为:
main 线程都执行完了 我是线程 0 我是线程 3 我是线程 4 我是线程 1 我是线程 2
我们引入
CountDownLatch
public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(5); for (int i = 0; i < 5; i++) { final int num = i; new Thread(()->{ System.out.println("我是线程 "+ Thread.currentThread().getName()); countDownLatch.countDown(); },num+"").start(); } countDownLatch.await(); System.out.println(Thread.currentThread().getName()+" 线程都执行完了"); }
结果为:
我是线程 0 我是线程 1 我是线程 2 我是线程 3 我是线程 4 main 线程都执行完了
小结:一个简单的线程间计数类,通过countDown来减少计数器,通过await等待计数器为零。可以用于统计并行线程计算的总时间。或者在zookeeper中作为线程之间的锁进行线程间通信。
-
CyclicBarrier(闸门&等待)
作用:作为一个“闸门“,当值到达一定的情况后进行一个操作。
代码:不加线程可以通过得到输入的数字进行动态精确唤醒。
public static void main(String[] args) throws InterruptedException { // 定义了参数为5 CyclicBarrier cyclicBarrier = new CyclicBarrier(5); for (int i = 0; i < 6; i++) { final int num = i; new Thread(()->{ // 通过cyclicBarrier.getParties()取出5 可以实现精确唤醒 if(num == cyclicBarrier.getParties()){ System.out.println("sdasdasda"+Thread.currentThread().getName()); } System.out.println("end thread " + Thread.currentThread().getName()); }).start(); } System.out.println("end"); }
方法2:可以通过累加,过一定阈值,进行操作。
public static void main(String[] args) throws InterruptedException { CyclicBarrier cyclicBarrier = new CyclicBarrier(6,()->{ System.out.println("这里是操作到达一定的阈值了 可以用System.gc进行手动垃圾回收"); }); for (int i = 0; i < 6; i++) { final int num = i; new Thread(()->{ try { System.out.println(" await " + Thread.currentThread().getName()); cyclicBarrier.await(); System.out.println(" await before " + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } },i+"").start(); } System.out.println("end"); }
结果为:
await 1 await 3 await 0 await 2 end await 4 await 5 这里是操作到达一定的阈值了 可以用System.gc进行手动垃圾回收 await before 5 await before 3 await before 1 await before 0 await before 4 await before 2
我们可以看到到达了6之后执行了异步任务,而主线程没有阻塞等待。但子线程出于阻塞等待状态
小结:这两个方法结合使用,可以动态的条件线程之间的关系,做到系统的动态变换。
-
Semaphore(限流&等待)
作用:可用来限流控制。可以将多的线程进入阻塞状态。
public static void main(String[] args) { Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 6; i++) { new Thread(()->{ long begin = System.currentTimeMillis(); try { System.out.println("acquire 之前 " + Thread.currentThread().getName()); // 得到了位置 semaphore.acquire(); TimeUnit.SECONDS.sleep(3); System.out.println("acquire 之后 " + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); }finally { long end = System.currentTimeMillis(); System.out.println("用时为"+(end-begin)+"线程名是" + Thread.currentThread().getName()); semaphore.release(); } }).start(); } }
运行结果
acquire 之前 Thread-0 acquire 之前 Thread-5 acquire 之前 Thread-2 acquire 之前 Thread-4 acquire 之前 Thread-3 acquire 之前 Thread-1 acquire 之后 Thread-2 acquire 之后 Thread-0 用时为3000线程名是Thread-0 acquire 之后 Thread-5 用时为3000线程名是Thread-2 用时为3000线程名是Thread-5 acquire 之后 Thread-3 用时为6001线程名是Thread-3 acquire 之后 Thread-1 用时为6002线程名是Thread-1 acquire 之后 Thread-4 用时为6002线程名是Thread-4
小结:可以看到所有的线程都在acquire的位置进入阻塞状态,3秒后前3个线程放行,又过了3秒,后三个线程放行了。
这个类可以用在限流中,减少服务的压力。用在共享资源互斥。