并发编程基础篇(三)之JUC常用线程类

CountDownLatch

类CountDownLatch是通过计数器的方式"组团"线程处理,然后回到主线程的一个辅助线程处理类。

通过设置要执行的线程数据,不为0时当前线程处于等待状态,直到设置的线程执行完。

使用方法,通过await方法锁定判断count数是否为0,通过countDown方法进行减1操作。

类方法:

用例:

public class CountDownLatchDemo extends Thread {
    private CountDownLatch countDownLatch;
    private int i;
    public CountDownLatchDemo(CountDownLatch countDownLatch,int i){
        this.countDownLatch = countDownLatch;
        this.i = i;
    }
    @Override
    public void run() {
        System.out.println("开始执行线程"+i);
        countDownLatch.countDown();
        System.out.println("线程"+i+"执行完毕");
    }
}
public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(3);
        CountDownLatchDemo downLatchDemo1 = new CountDownLatchDemo(countDownLatch,1);
        downLatchDemo1.start();
        CountDownLatchDemo downLatchDemo2 = new CountDownLatchDemo(countDownLatch,2);
        downLatchDemo2.start();
        CountDownLatchDemo downLatchDemo3 = new CountDownLatchDemo(countDownLatch,3);
        downLatchDemo3.start();
        countDownLatch.await();
        System.out.println("主线程执行完毕");
    }

执行结果:

开始执行线程1

线程1执行完毕

开始执行线程2

线程2执行完毕

开始执行线程3

线程3执行完毕

主线程执行完毕

备注:await方法可设时间,也可不设,如果设置时间,时间到期后会自动释放进入到主线程

CyclicBarrier

说明: 让一组线程到达某个屏障,然后被阻塞,一直到最后一个线程到达屏障,然后屏障开放,所有被阻塞的线程才能继续执行,计数器与线程数相等。

CyclicBarrier(int parties, Runnable barrierAction) 当时使用这个构造函数时,barrierAction定义的任务会执行

用例:

public class CyclicBarrierDemo extends Thread {
    private CyclicBarrier cyclicBarrier;
    private int i;
    public CyclicBarrierDemo(CyclicBarrier cyclicBarrier,int i){
        this.cyclicBarrier = cyclicBarrier;
        this.i = i;
    }

    @Override
    public void run() {
        System.out.println("开始执行线程"+i);
        try {
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println("线程"+i+"执行完毕");
    }
}
public class CyclicBarrierAction extends Thread {

    @Override
    public void run(){
        System.out.println("所有线程准备完毕后执行");
    }
}
 public static void main(String[] args) throws InterruptedException {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3,new CyclicBarrierAction());
        CyclicBarrierDemo cyclicBarrierDemo1 = new CyclicBarrierDemo(cyclicBarrier,1);
        cyclicBarrierDemo1.start();
        CyclicBarrierDemo cyclicBarrierDemo2 = new CyclicBarrierDemo(cyclicBarrier,2);
        cyclicBarrierDemo2.start();
        CyclicBarrierDemo cyclicBarrierDemo3 = new CyclicBarrierDemo(cyclicBarrier,3);
        cyclicBarrierDemo3.start();
        System.out.println("主线程执行完毕");
    }

执行结果:

开始执行线程1

开始执行线程2

开始执行线程3

所有线程准备完毕后执行

线程3执行完毕

线程1执行完毕

线程2执行完毕

备注:

CountDownLatch和CyclicBarrier主要区别在于countDownLatch执行完后回到当前主线程,CyclicBarrier则回到指定线程

ForkJoin

ForkJoin框架的本质是一个用于并行执行任务的框架, 能够把一个大任务分割成若干个小任务,最终汇总每个小任务结果后得到大任务的计算结果。

典型应用

二分搜索

大整数乘法

Strassen矩阵乘法

棋盘覆盖

合并排序

快速排序

线性时间选择

汉诺塔

ForkJoin框架的实现

ForkJoin框架中一些重要的类如下所示。

ForkJoinPool 框架中涉及的主要类如下所示。

1.ForkJoinPool类

实现了ForkJoin框架中的线程池,由类图可以看出,ForkJoinPool类实现了线程池的Executor接口。

我们也可以从下图中看出ForkJoinPool的类图关系。

其中,可以使用Executors.newWorkStealPool()方法创建ForkJoinPool。

ForkJoinPool中提供了如下提交任务的方法。

public void execute(ForkJoinTask<?> task)
public void execute(Runnable task)
public <T> T invoke(ForkJoinTask<T> task)
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) 
public <T> ForkJoinTask<T> submit(ForkJoinTask<T> task)
public <T> ForkJoinTask<T> submit(Callable<T> task)
public <T> ForkJoinTask<T> submit(Runnable task, T result)
public ForkJoinTask<?> submit(Runnable task)

2.ForkJoinWorkerThread类

实现ForkJoin框架中的线程。

3.ForkJoinTask<V>类

ForkJoinTask封装了数据及其相应的计算,并且支持细粒度的数据并行。ForkJoinTask比线程要轻量,ForkJoinPool中少量工作线程能够运行大量的ForkJoinTask。

ForkJoinTask类中主要包括两个方法fork()和join(),分别实现任务的分拆与合并。

fork()方法类似于Thread.start(),但是它并不立即执行任务,而是将任务放入工作队列中。跟Thread.join()方法不同,ForkJoinTask的join()方法并不简单的阻塞线程,而是利用工作线程运行其他任务,当一个工作线程中调用join(),它将处理其他任务,直到注意到目标子任务已经完成。

我们可以使用下图来表示这个过程。

ForkJoinTask有3个子类:

RecursiveAction:无返回值的任务。

RecursiveTask:有返回值的任务。

CountedCompleter:完成任务后将触发其他任务。

4.RecursiveTask<V> 类

有返回结果的ForkJoinTask实现Callable。

5.RecursiveAction类

无返回结果的ForkJoinTask实现Runnable。

6.CountedCompleter<T> 类

在任务完成执行后会触发执行一个自定义的钩子函数。

用例:

public class SumArray {

    //产生一个随机数组
    private static int[] array = MakeArray.makeArray();
    //阈值
    private final static int POINT = MakeArray.ARRAY_LENGTH / 10;


    //有返回值的任务
    private static class SumTask extends RecursiveTask<Integer> {

        //要累加的源数组
        private int[] src;
        //开始角标
        private int startIndex;
        //结束角标
        private int endIndex;

        public SumTask(int[] src, int startIndex, int endIndex) {
            this.src = src;
            this.startIndex = startIndex;
            this.endIndex = endIndex;
        }

        //实现具体的累加逻辑和任务分割逻辑
        @Override
        protected Integer compute() {
            //不满足阈值的时候,这里面的逻辑也是当满足阈值的时候,递归执行的逻辑
            if (endIndex - startIndex < POINT) {
                int count = 0;
                for (int i = startIndex; i <= endIndex; i++) {
                    count += src[i];
//                    SleepTools.ms(1);
                }
                return count;

                //满足阈值的时候,需要分割任务,然后交给forkjoinpool去执行任务
            } else {

                //当需要分割的时候,采用折中法进行分割
                //startIndex.......mid.......endIndex
                int mid = (startIndex + endIndex) / 2;

                //左任务
                SumTask leftTask = new SumTask(src, startIndex, mid);
                //右任务
                SumTask rigthTask = new SumTask(src, mid + 1, endIndex);

                //交给forkjoinpool去执行任务
                invokeAll(leftTask, rigthTask);

                //将执行结果返回
                return leftTask.join() + rigthTask.join();
            }
        }
    }


    private static void testForkJoin() {
        //创建ForkJoinPool池
        ForkJoinPool forkJoinPool = new ForkJoinPool();

        long startTime = System.currentTimeMillis();
        SumTask task = new SumTask(array, 0, array.length - 1);

        //这个方法是阻塞的,是同步的
        forkJoinPool.invoke(task);

        long endTime = System.currentTimeMillis();
        System.out.println("采用forkjoin执行结果是:" + task.join() + "---------用时:" + (endTime - startTime));
    }

    private static void testFor() {
        long startTime = System.currentTimeMillis();
        int count = 0;
        for (int i = 0; i < array.length; i++) {
            count += array[i];
//            SleepTools.ms(1);
        }
        long endTime = System.currentTimeMillis();
        System.out.println("采用for循环执行结果是:" + count + "---------用时:" + (endTime - startTime));
    }


    public static void main(String[] args) {
        testForkJoin();
        testFor();
    }


}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值