JCU-线程栅栏CountDowLatch与CyclicBarrier,Exchanger

CountDowLatch
  1. 用法
    (1) CountDowLatch 用来同步一个或多个线程, 强制这些线程等待由其它线程执行的一组操作完成
    (2) 可以向 CountDowLatch 设置一个初始计数, 任何在这个对象上调用wait()方法的线程都会阻塞挂起, 等待内部计数值为0 ; 其它线程在完成工作时, 可以调用 CountDowLatch 的countDown()方法来减小计数值
    (3) CountDowLatch的计数值不能被重置, 因此, 一旦一个CountDowLatch对象的计数值到达0, 就再也无法提供阻塞功能; 要使用可重置计数版本的CountDowLatch, 可以使用CyclicBarrier

  2. 示例
    Task类随机休眠一段时间, 模拟这部分工作的完成; WaitingTask表示系统中必须要等待的部分, 要等待问题的初始部分完成为止
    在这里插入图片描述

CyclicBarrier
  1. CyclicBarrier表示什么意思
    CyclicBarrier适合这样的情况: 有一个任务A需要等待其它一组任务Group全都完成之后再执行; 当任务A执行完毕后, A再次等待任务组Group全部执行完再执行. 相当于这是一个一组任务和一个任务之间交替等待的过程

  2. CyclicBarrier构造函数
    构造器: public CyclicBarrier(int parties, Runnable barrierAction), 有两个参数
    (1) parties: CyclicBarrier计数器初始值(也是线程组的线程个数)
    (2) barrierAction: 当计数器值变为0时执行该线程(同时线程组所有线程执行完毕)

    CyclicBarrier只有一个方法: await(), 当线程组的一个线程执行完后, 要调用await()方法让CyclicBarrier内部的计数器值-1, 当内部计数值变为0时, barrierAction线程就会执行, 当这个线程执行完后, 计数器重置为初始值, 且调用await()的线程组中的线程会返回继续执行

  3. 使用CyclicBarrier进行仿真赛马游戏
    游戏说明: 一个赛马比赛, 跑到长度为FINISH_LINE, 有nHorses匹马进行比赛. 每次让每匹马随机的跑n步, 循环进行, 直到有一匹马先跑完整个赛道获得冠军
    (1) 马匹

class Horse implements Runnable {
    static AtomicInteger count = new AtomicInteger();
    static Random r = new Random(47);
    private final int id = count.getAndIncrement();

    private int strides; // 已经跑了多少步
    private CyclicBarrier barrier;

    public Horse(CyclicBarrier barrier) {
        this.barrier = barrier;
        this.strides = 0;
    }

    public int getStrides() {
        return strides;
    }

    @Override
    public void run() {
        try {
            // 一次循环向前跑几步, 然后等待所有马向前跑一次
            while (!Thread.interrupted()) {
                strides += r.nextInt(10);
                barrier.await();   // 跑完一次, 进入等待
            }
        } catch (InterruptedException | BrokenBarrierException e) {
            e.printStackTrace();
        }
    }

    @Override
    public String toString() {
        return "Horse{" +
                "id=" + id +
                '}';
    }

    // 打印该匹马已经跑过的距离
    public void printTrace() {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < this.strides; i++) {
            sb.append("*");
        }
        sb.append(this.id);
        System.out.println(sb.toString());
    }
}

(2) 比赛

public class HorseRace {
    static final int RACE_LENGTH = 20;   // 跑道长度
    ArrayList<Horse> horses = new ArrayList<Horse>();
    private CyclicBarrier barrier;        // 用于同步的栅栏
    private ExecutorService service;

    public HorseRace(int horseCount) {
        // 初始化线程池
        this.service = Executors.newFixedThreadPool(horseCount);
        // 初始化 CyclicBarrier
        this.barrier = this.initBarrier(horseCount);
        // 初始化马匹
        for (int i = 0; i < horseCount; i++) {
            horses.add(new Horse(this.barrier));
        }
    }

    public void start() {
        for (Horse horse : horses) {
            service.execute(horse);
        }
    }

    private CyclicBarrier initBarrier(int horseCount) {
        return new CyclicBarrier(horseCount, new Runnable() {  // CyclicBarrier 一次循环归0后执行的操作
            @Override
            public void run() {
                System.out.println("[本轮跑步结束 ---------------------------------------------]");
                // 一轮结束后, 打印马匹的轨迹
                for (Horse horse : horses) {
                    horse.printTrace();
                }
                // 判断是否有马匹胜出
                for (Horse horse : horses) {
                    if (horse.getStrides() >= RACE_LENGTH) {
                        System.out.println(horse + "胜出");
                        service.shutdownNow();
                        return;
                    }
                }
                // 每轮跑步有停顿时间
                try {
                    Thread.sleep(2 * 1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public static void main(String[] args) {
        new HorseRace(7).start();
    }
}
Exchanger
  1. Exchanger用处
    Exchanger是2个线程之间交换对象的栅栏; 它的典型应用场景是: 一个线程在创建对象.这些对象的床架代价高昂, 而另一个线程在消费对象. 通过这种方式, 可以有更多的对象在被创建的同时被消费
  2. 方法
    当一个线程调用exchange()方法时将会阻塞挂起, 直到另一个线程也调用了exchange()方法时, 2个线程的exchange()可以运行完毕, 而持有的对象将会交换
  3. 仿真
    如下创建了2个线程的单一Exchanger, Producer用于在CopyOnWriteList中塞入数据, Consumer使用Exchanger和Producer交换构造好的list消费其中的对象(使用remove元素的方式模拟消耗对象). CopyOnwriteList不会产生并发修改异常, 但会频繁触发gc
    在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值