Java并发工具类(JUC)汇总(三)工具类


三、工具类

1.Semaphore

1.1 简介


 Semaphore是信号量,允许多个线程访问临界区,可以控制访问临界区的数量。实现逻辑为核心是初始化一个计数器表示可用许可的数目,请求资源时减去申请许可的个数,可用许可数目小于0时阻塞,释放资源时加上释放许可的个数,并唤醒对应个数的线程。通常用作控制池化资源(连接池、对象池)的访问,达到限流的效果。

 

1.2 方法介绍




   /**
     * 构造方法
     * @param permits 可用许可的初始数目
     */
    public Semaphore(int permits)


    /**
     * 构造方法
     * @param permits 可用许可的初始数目
     * @param fair 是否公平
     */
    public Semaphore(int permits, boolean fair)


    /**
     * 请求资源
     * @throws InterruptedException 中断异常
     */
    public void acquire() throws InterruptedException

    /**
     * 请求给定数目的资源
     * @param permits 请求的许可数目
     * @throws InterruptedException
     */
    public void acquire(int permits) throws InterruptedException

    /**
     * 释放资源
     */
    public void release()


    /**
     * 释放给定数目的资源
     * @param permits 释放的许可数目
     */
    public void release(int permits)


 

1.3 使用样例


 模拟抢车位的情况,共有两个车位,有五辆车在抢车位


public static class CarThread extends Thread {

    private final Semaphore semaphore;

    public CarThread(Semaphore semaphore) {
        this.semaphore = semaphore;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"开始抢车位");
        try {
            semaphore.acquire();
            System.out.println(Thread.currentThread().getName()+"已抢到车位");
            Thread.sleep(100);
            System.out.println(Thread.currentThread().getName()+"停车结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }finally {
            semaphore.release();
        }

    }
}

public static void main(String[] args){
    //模拟只有两个车位的情况
    Semaphore semaphore = new Semaphore(2, true);
    for (int i = 0; i < 5; i++) {
        new CarThread(semaphore).start();
    }

}

 

1.4 注意事项


 release不需在acquire后调用,release实际执行的是增加许可数量的操作与acquire没有强关联

 

2.CountDownLatch

2.1 简介


 CountDownLatch是计数器,没有公平或不公平之分,使用await阻塞直到等待计数器变为0,变为0后所有await阻塞的线程都会唤醒,使用countDown使计数器减一,CountDownLatch的使用场景通常用于一个线程需要等待其他线程的执行结果的情况,如需等待所有运动员就绪才开始比赛

 

2.2 方法介绍



/**
     * 构造方法
     * @param count 初始化计数器数量
     */
    public CountDownLatch(int count)

    /**
     * 计数器减一,为0时唤醒所有阻塞的线程
     */
    public void countDown()

    /**
     * 阻塞线程
     * @throws InterruptedException 中断异常
     */
    public void await() throws InterruptedException

 

2.3 使用样例


 主线程等待子线程的任务完成后执行剩下的步骤


public static void main(String[] args) {

        CountDownLatch countDownLatch = new CountDownLatch(2);
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程一处理");
                countDownLatch.countDown();
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程二处理");
                countDownLatch.countDown();
            }
        }).start();


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

        System.out.println("处理完成");
    }
    

 

3.CyclicBarrier

3.1 简介


 CyclicBarrier是循环栅栏,调用一次await计数器减一,计数器为0后最后一个线程唤醒所有线程,并执行回调方法,重置计数器,开始下一个轮次(Generation),适用于线程间相互等待直到所有线程执行完毕后才继续执行的情况,如面试中等待所有人到齐才开始笔试,所有人笔试完成后才开始面试

 

3.2 方法介绍




 /**
     * 构造函数
     * @param parties 参与者数量
     * @param barrierAction 每一轮执行完毕回调函数
     */
    public CyclicBarrier(int parties, Runnable barrierAction)

    /**
     * 构造函数
     * @param parties 参与者数量
     */
    public CyclicBarrier(int parties)

    /**
     * 阻塞等待并使计数器减一
     * @return 当前线程的到达索引,其中索引getParties() - 1表示第一个到达,0表示最后一个到达
     * @throws InterruptedException 中断异常
     * @throws BrokenBarrierException 如果当前线程正在等待时,另一个线程被中断或超时,或者屏障被重置,或者在await被调用时屏障被打破,或者屏障操作(如果存在)由于异常而失败
     */
    public int await() throws InterruptedException, BrokenBarrierException

    /**
     * 阻塞等待并使计数器减一,可超时
     * @param timeout 超时时间
     * @param unit 超时单位
     * @return
     * @throws InterruptedException 可中断异常
     * @throws BrokenBarrierException 如果当前线程正在等待时,另一个线程被中断或超时,或者屏障被重置,或者在await被调用时屏障被打破,或者屏障操作(如果存在)由于异常而失败
     * @throws TimeoutException 超时异常
     */
    public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException


 

3.3 使用样例


 所有考生等待考试开始,并等待考试结束


 public static class IntervieweeThread extends Thread{

     private final CyclicBarrier cyclicBarrier;

     public IntervieweeThread(CyclicBarrier cyclicBarrier) {
         this.cyclicBarrier = cyclicBarrier;
     }

     @Override
     public void run() {
         System.out.println(Thread.currentThread().getName()+"到达考试地点,开始等待考试开始");
         try {
             cyclicBarrier.await();
         } catch (InterruptedException e) {
             e.printStackTrace();
         } catch (BrokenBarrierException e) {
             e.printStackTrace();
         }
         System.out.println(Thread.currentThread().getName()+"做完试卷,等待考试结束");

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

 public static void main(String[] args) {
     //5个考生
     CyclicBarrier cyclicBarrier = new CyclicBarrier(5, new Runnable() {
         @Override
         public void run() {
             System.out.println("本轮执行完毕");
         }
     });
     for (int i = 0; i < 5; i++) {
         new IntervieweeThread(cyclicBarrier).start();
     }
 }
    

 

3.4 注意事项


 CyclicBarrier 会响应中断,被中断的线程会唤醒所有其他的被阻塞线程,计数器重置为初始值

 

4.Exchanger

4.1 简介


 Exchanger是一个交换器,用于两个或多个线程之间相互交换数据,实现原理为一个线程判断slot为空后(数据交换槽)放入数据后阻塞,另一个线程判断slot不为空,放入数据唤醒数据对应的线程,并阻塞自己等待被唤醒。为了提高并发度,引入arena node数组,使多个线程间在不同的槽上进行访问,提高并发度。

 

4.2 方法介绍



  /**
   *  交换数据
   * @param x 待交换的数据
   * @return 交换回来的数据
   * @throws InterruptedException
   */
  public V exchange(V x) throws InterruptedException 


 

4.3 使用样例


 两个线程间交换数据


public static void main(String[] args) {
     Exchanger<String> exchanger = new Exchanger<String>();
     new Thread(new Runnable() {
         @Override
         public void run() {
             try {
                 String data = exchanger.exchange("线程一数据");
                 System.out.println("线程一交换后的数据:"+data);

             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
     }).start();

     new Thread(new Runnable() {
         @Override
         public void run() {
             try {
                 String data = exchanger.exchange("线程二数据");
                 System.out.println("线程二交换后的数据:"+data);

             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
     }).start();
 }
    

 

5.Phaser

5.1 简介


 Phaser是阶段器,一个可重用的同步屏障,在功能上类似于CyclicBarrier和CountDownLatch,但支持动态增删参与者的数量以及层级扩展(支持添加父Phaser)。如下图所示,每执行一个周期会记录phase的值,表示不同的执行阶段。

在这里插入图片描述

 

5.2 方法介绍



    /**
     * 构造方法
     * 默认参与者数量为0
     */
    public Phaser() {
        this(null, 0);
    }

    /**
     * 构造方法
     *
     * @param parties 参与者数量为0
     */
    public Phaser(int parties) {
        this(null, parties);
    }

    /**
     * 构造方法
     *
     * @param parent 父Phaser 会自动向父Phaser注册自己
     */
    public Phaser(Phaser parent) {
        this(parent, 0);
    }

    /**
     * 构造方法
     *
     * @param parent  父Phaser 会自动向父Phaser注册自己
     * @param parties 参与者数量为0
     */
    public Phaser(Phaser parent, int parties)



    /**
     * 注册一个新参与者使原数量+1
     *
     * @return
     */
    public int register()

    /**
     * 批量注册若干个参与者 使原数量+parties
     *
     * @param parties 参与者数量
     * @return
     */
    public int bulkRegister(int parties)


    /**
     * 当前线程到达当前阶段 类似于countDown
     *
     * @return arrival phase number 阶段编号
     */
    public int arrive()

    /**
     * 当前线程到达当前阶段并撤销注册
     *
     * @return phase阶段编号
     */
    public int arriveAndDeregister()

    /**
     * 使当前线程到达phaser并等待其他任务到达,等价于awaitAdvance(arrive())。
     * 如果需要等待中断或超时,可以使用awaitAdvance方法完成一个类似的构造。
     * 如果需要在到达后取消注册,可以使用awaitAdvance(arriveAndDeregister())。
     * arriveAndAwaitAdvance类似 CyclicBarrier await
     *
     * @return phase阶段编号
     */
    public int arriveAndAwaitAdvance()


    /**
     * 阻塞当前阶段编号线程,编号不匹配不阻塞
     * @param phase 当前 phase 阶段编号
     * @return 下一个 phase 阶段编号
     */
    public int awaitAdvance(int phase)



    /**
     * 可中断的阻塞
     * @param phase 当前 phase 阶段编号
     * @return 下一个 phase 阶段编号
     * @throws InterruptedException 中断异常
     */
    public int awaitAdvanceInterruptibly(int phase) throws InterruptedException

    /**
     *  超时阻塞
     * @param phase 当前 phase 阶段编号
     * @param timeout 超时时间
     * @param unit 超时时间单位
     * @return 下一个 phase 阶段编号
     * @throws InterruptedException 中断异常
     * @throws TimeoutException 超时异常
     */
    public int awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit)
            throws InterruptedException, TimeoutException



    /**
     * 返回当前阶段号,终止时为负数,最大值为Integer.MAX_VALUE,超过最大值清零
     * @return 当前阶段号
     */
    public final int getPhase()

    /**
     * 当前phaser是否被终止
     */
    public boolean isTerminated()

    /**
     * 终止当前phaser,已注册的parties不受影响,如果是分层结构,则终止所有phaser,通常用于异常后协调恢复
     */
    public void forceTermination()


 

5.3 使用样例


5.3.1 替代CountDownLatch

 public static void main(String[] args) {

     Phaser phaser = new Phaser(2);
     new Thread(new Runnable() {
         @Override
         public void run() {
             System.out.println("线程一处理");
             phaser.arrive();
         }
     }).start();
     new Thread(new Runnable() {
         @Override
         public void run() {
             System.out.println("线程二处理");
             phaser.arrive();
         }
     }).start();

     phaser.awaitAdvance(phaser.getPhase());

     System.out.println("处理完成");
 }
    

 

5.3.2 替代CyclicBarrier

public static class IntervieweeThread extends Thread {

    private final Phaser phaser;

    public IntervieweeThread(Phaser phaser) {
        this.phaser = phaser;
    }

    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + "到达考试地点,开始等待考试开始");
        phaser.arriveAndAwaitAdvance();
        System.out.println(Thread.currentThread().getName() + "做完试卷,等待考试结束");
        phaser.arriveAndAwaitAdvance();
    }
}


public static class MyPhaser extends Phaser {

    public MyPhaser(int parties) {
        super(parties);
    }

    /**
     * 可重写方法,当一个阶段的所有线程都到达时,执行该方法
     * @param phase 当前阶段
     * @param registeredParties 当前的注册方数量
     * @return 返回true时终止执行 返回false phase加1
     */
    @Override
    protected boolean onAdvance(int phase, int registeredParties) {
        switch (phase) {
            case 0 :
                System.out.println("考试开始");
                return false;
            case 1:
                System.out.println("考试结束");
                return false;
            default:
                //终止
                return true;
        }
    }
}

public static void main(String[] args) {
    //5个考生
    Phaser phaser = new MyPhaser(5);
    for (int i = 0; i < 5; i++) {
        new IntervieweeThread(phaser).start();
    }
}
    

 

5.3.3 新特性

 动态添加参与者及不等待其他线程直接执行


 public static class IntervieweeThread extends Thread {

     private final Phaser phaser;

     public IntervieweeThread(Phaser phaser) {
         this.phaser = phaser;
     }

     @Override
     public void run() {
         try {
             Thread.sleep(500);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         System.out.println(Thread.currentThread().getName() + "到达考场,等待其他同学到场");
         phaser.arriveAndAwaitAdvance();

         try {
             Thread.sleep(1000);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
         phaser.arriveAndDeregister();
         System.out.println(Thread.currentThread().getName() + "做完试卷,直接走");
     }
 }



 public static void main(String[] args) {
     //5个考生
     Phaser phaser = new Phaser(5);
     for (int i = 0; i < 5; i++) {
         new IntervieweeThread(phaser).start();
     }

     //新来了一个考生
     phaser.register();
     new IntervieweeThread(phaser).start();

     //新来了三个考生
     phaser.bulkRegister(3);
     new IntervieweeThread(phaser).start();
     new IntervieweeThread(phaser).start();
     new IntervieweeThread(phaser).start();


 }

    

 

 终止


public static void main(String[] args) {
        Phaser phaser = new Phaser(2);

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("阻塞等待 阶段"+phaser.getPhase());
                phaser.arriveAndAwaitAdvance();
                System.out.println("等待完成 阶段"+phaser.getPhase());
                System.out.println("再次阻塞等待 阶段"+phaser.getPhase());
                phaser.arriveAndAwaitAdvance();
                System.out.println("再次等待完成 阶段"+phaser.getPhase());
            }
        }).start();

        System.out.println("终止");
        phaser.forceTermination();

 可以看到当发出终止时,phase的值为负数,不会执行阻塞逻辑

终止
阻塞等待 阶段-2147483648
等待完成 阶段-2147483648
再次阻塞等待 阶段-2147483648
再次等待完成 阶段-2147483648

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值