Java基础之Fork-Join 框架、信号量、障栅(屏障)、同步队列、交换器、倒计时、同步器常用类

随笔,笔记

Fork-Join 框架

fork-join 可以将一个任务分解为 多个子任务。适用于 密集型计算。

提供了两个抽象类:

  • RecursiveTask<T>:计算会生成一个类型为 T 的结果。
  • RecursiveAction:计算不会生成结果。

实现 这个两个抽象类的其中一个,并 重载 compute 方法来生成并调用 子任务。

public class Counter extends RecursiveTask<Integer> {
    private int counter;

    public Counter(int counter) {
        this.counter = counter;
    }

    @Override
    protected Integer compute() {
    	// 进行 递归 分解
        if (this.counter == 100) {
            Counter first = new Counter(counter - 1);
            Counter second = new Counter(counter + 1);
            // 接收 多个任务,并阻塞,知道所有任务 都已经完成
            invokeAll(first, second);
            // join 方法返回 子任务 计算的结果
            return first.join() + second.join();
        }
        return this.counter;
    }
}

测试

public class MainTest {

    public static void main(String[] args) {
        // 创建 任务
        Counter counter = new Counter(100);
        // 
        ForkJoinPool pool = new ForkJoinPool();
        // 执行给定的任务,完成后返回其结果。
        Integer number = pool.invoke(counter);
        System.out.println(number);
    }
}

感觉 fork-join 考验 递归思想,归并思想。

信号量

  • 概念上讲,一个信号量管理许多的许可证。为了通过信号量,线程通过调用 acquire 请求许可。
  • 信号量是维护一个计数。由于许可证的数量是固定的,所以可以限制通过的线程数量。
  • 其他线程可以 通过 release 释放许可证。不是必须由调用它的线程释放,任何线程都可以释放任意数目的许可证。
  • Dijkstra 指出 信号量可以被有效地实现,并且有足够的能力解决许多常见的线程同步问题。

倒计时门栓

  • 一个倒计时门栓,让一个线程集等待 ,直到计时变为 0。
  • 倒计时门栓 是一次性,一旦变为 0,就不能再重用了。
public class MainTest {

    public static void main(String[] args) {
        CountDownLatch latch = new CountDownLatch(3);

        new Thread(()->{
            try {
                // 当 计数器 不为 0 时,等待
                // 当计数器 变为 0 继续运行
                latch.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        },"线程1").start();

        new Thread(()->{
            System.out.println(Thread.currentThread().getName());
            // 计数器 减一
            latch.countDown();
        },"线程2").start();

        new Thread(()->{
            System.out.println(Thread.currentThread().getName());
            // 计数器 减一
            latch.countDown();
        },"线程3").start();

        new Thread(()->{
            System.out.println(Thread.currentThread().getName());
            // 计数器 减一
            latch.countDown();
        },"线程4").start();
    }
}

障栅(屏障)

  • 障栅,CyclicBarrier类实现的一个集结点。
  • 画一条线,线程运行到 线的位置,就等一等,等所有线程都到达这条线,就解除屏障,继续运行。
  • 适合 那些把 所有部分都准备好,把结果组合起来 的应用。
public class MainTest {

    public static void main(String[] args) {
        // 构建一个 屏障,指定需要参与的线程数,本次是指定的 3
        CyclicBarrier barrier = new CyclicBarrier(3);

        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"执行完成,进入等待");
            try {
                // 设定一个 屏障
                barrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"线程结束");
        },"线程1").start();

        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"执行完成,进入等待");
            try {
                barrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"线程结束");
        },"线程2").start();

        new Thread(()->{
            System.out.println(Thread.currentThread().getName()+"执行完成,进入等待");
            try {
                barrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"线程结束");
        },"线程3").start();

    }
}
  • 线程 凑够 指定的线程数,就接触屏障。
  • 可以提供一个屏障动作,当所有线程到达屏障的时候 执行这个动作
// 构建一个 屏障,指定需要参与的线程数,本次是指定的 3
CyclicBarrier barrier = new CyclicBarrier(2,()->{
    System.out.println("凑够了");
});
  • 障栅 是循环的,当等待线程被释放后会被 重用。

交换器

  • 当两个线程在同一个数据缓冲区的两个实例上工作的时候,可以使用交换器。
  • 一个线程向缓冲区填入数据,另一个线程消耗这些数据。当他们都完成以后,相互交换缓冲区。

代码理解:

public class MainTest {

    public static void main(String[] args) {

        // 构建 交换器
        Exchanger<Integer> exchanger = new Exchanger<>();

        new Thread(()->{
            Integer integer = 100;
            try {
                System.out.println(Thread.currentThread().getName()+"线程开始交换:"+integer);
                // 进行交换 ,并返回 交换之后的值
                integer = exchanger.exchange(integer);
                System.out.println(Thread.currentThread().getName()+"线程交换完成:"+integer);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程1").start();

        new Thread(()->{
            Integer integer = 200;
            try {
                System.out.println(Thread.currentThread().getName()+"线程开始交换:"+integer);
                // 进行交换 ,并返回 交换之后的值
                integer = exchanger.exchange(integer);
                System.out.println(Thread.currentThread().getName()+"线程交换完成:"+integer);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程2").start();
    }
}
  • 线程数量 必须要 偶数,为奇数时,有一个线程 交换,一直等待。
  • 可重复使用

同步队列

  • 同步队列是一种将生产者与消费者线程配对的机制。
  • 当一个线程 调用SynchronousQueue 的put 方法时,它会阻塞直到另一个线程调用 take 方法为止。
public class MainTest {

    public static void main(String[] args) {

        SynchronousQueue<Integer> queue = new SynchronousQueue<>();

        new Thread(()->{
            Integer integer = 100;
            try {
                System.out.println(Thread.currentThread().getName()+"线程添加一个值:"+integer);
                queue.put(integer);
                System.out.println(Thread.currentThread().getName()+"线程 解除 阻塞");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程1").start();

        new Thread(()->{
            try {
                Thread.sleep(3000);
                Integer integer = queue.take();
                System.out.println(Thread.currentThread().getName()+"线程 获取此值:"+integer);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"线程2").start();
    }
}
  • SynchronousQueue 的 size 方法总是 返回 0;
作用场景
CyclicBarrier允许线程集等待,直到其中预定数目的线程到达一个 公共障栅,然后结束线程的等待,继续执行当大量线程需要在它们结果可用之前完成时
Phaser和CyclicBarrier 差不多,但是它允许动态改变预定的线程数目Java7 引入
CountDownLatch允许线程集等待直到计数器减为 0当一个或多个线程需要等待直到指定数目的事件发生
Exchanger允许两个线程交换对象,在 对象准备好时当两个线程工作在同一数据结构的两个实例上的时候,一个向实例添加数据而另一个从实例清除数据
Semaphore允许线程集等待直到被允许继续执行为止限制访问资源的线程总数。
SynchronousQueue允许一个线程 把对象 交给另一个线程在没有显示同步的情况下,当两个线程准备好将一个对象从一个线程传递到另一个时
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值