线程学习(三)之线程的并发工具类

本文详细介绍了Java并发工具类中的Fork-Join框架,包括分而治之的概念,同步和异步用法。此外,还探讨了CountDownLatch、CyclicBarrier的使用及其区别,以及Semaphore、Exchange、Callable和FutureTask等并发工具的用途和特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Fork-Join

什么是分而治之

规模为N的问题,N<阈值,直接解决,N>阈值,将N分解成K个小规模问题,子问题互相独立,与原问题形式相同,将子问题的解合并得到原问题的解

动态规范:规模为N的问题,N<阈值,直接解决,N>阈值,将N分解成K个小规模问题,子问题互相有联系,与原问题形式相同,将子问题的解合并得到原问题的解。

在这里插入图片描述流程图
RecursiveTask:指定任务有返回值的时候继承这个类
RecursiveAction:指定任务没有返回值的的时候继承这个类
使用时需覆盖两个类中的compute方法

Fork-Join的同步用法
package com.day03;

import java.util.Random;

/**
 * 产生整型数据
 */
public class MakeArray {
    //数组长度
    public static final int ARRAY_LENGTH = 4000;
    public static int [] makeArray(){

        //new一个随机数发生器
        Random r = new Random();
        int [] result = new int[ARRAY_LENGTH];
        for (int i = 0; i <ARRAY_LENGTH ; i++) {
            //用随机数填充数组
            result[i] = r.nextInt(ARRAY_LENGTH*3);
        }
        return result;
    }
}

package com.day03;

import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveTask;

public class SumArray {
    private static class SumTask extends RecursiveTask<Integer>{
        private final static int THRESHOLD = MakeArray.ARRAY_LENGTH/10;
        private int[] src;//表示我们要实际统计的数组
        private int fromIndex;//开始统计的下标
        private int toIndex;//统计到哪里结束的下标

        public SumTask(int[] src, int fromIndex, int toIndex) {
            this.src = src;
            this.fromIndex = fromIndex;
            this.toIndex = toIndex;
        }

        @Override
        protected Integer compute() {
           if (toIndex-fromIndex<THRESHOLD){
               int count = 0;
               for (int i = fromIndex; i < toIndex; i++) {
                   try {
                       Thread.sleep(100);
                       count = count+src[i];
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   }
               }
               return count;
           }else {
               int mid = (fromIndex+toIndex)/2;
               SumTask left = new SumTask(src,fromIndex,mid);
               SumTask right = new SumTask(src,mid+1,toIndex);
               invokeAll(left,right);
               return left.join() + right.join();
           }
        }
    }

    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool();
        int [] src = MakeArray.makeArray();

        SumTask innerFind = new SumTask(src,0,src.length);

        long start = System.currentTimeMillis();

        pool.invoke(innerFind);//同步调用

        System.out.println("值:"+innerFind.join()+"时间:"+(System.currentTimeMillis()-start)+"ms");


    }
}

总结

有时候使用多线程未必比单线程快。就好比上面代码进行简单的累加运算。在总数比较小时,单线程就比较快。多线程比较慢,原因是开启了个多线程,线程之间进行切换(时间片轮转机制)消耗了时间。所以选择单线程还是多线程,需要具体的任务详情。

Fork-Join的异步用法
package com.day03;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;

/**
 * 遍历指定目录(含子目录)找寻指定类型文件
 */
public class FindDirsFiles extends RecursiveAction {
    private File path;

    public FindDirsFiles(File path) {
        this.path = path;
    }

    public static void main(String[] args) {
        try {
            //用一个ForkJoinPool实现调度总任务
            ForkJoinPool pool = new ForkJoinPool();
            FindDirsFiles task = new FindDirsFiles(new File("F:/"));
            pool.execute(task);  //异步调用
            System.out.println("Task is Running...");
            Thread.sleep(1);
            int otherWork = 0;
            for (int i = 0; i <100 ; i++) {
                otherWork = otherWork + i;
            }
            System.out.println("Main Thread done sth....,otherWork="+otherWork);
            task.join();//阻塞主线程,让ForkJoinPool进行工作
            System.out.println("Task end");
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    @Override
    protected void compute() {
        List<FindDirsFiles> subTasks = new ArrayList<>();

        File [] files = path.listFiles();
        if (files!=null){
            for (File file:files) {
                if (file.isDirectory()){
                    subTasks.add(new FindDirsFiles(file));
                }else{
                    //遇到文件,检查
                    if(file.getAbsolutePath().endsWith("txt")){
                        System.out.println("文件:"+file.getAbsolutePath());
                    }
                }
            }
            if(!subTasks.isEmpty()){
                for (FindDirsFiles subTask: invokeAll(subTasks)) {
                    subTask.join();//等待所有子任务完成
                }
            }
        }

    }
}

常用的并发工具类

CountDownLatch

作用:是一个线程等待其他线程完成工作后再执行的工具类。加强版join。、
await 方法用来等待,countDown负责计数器减一

CyclicBarrier

让一组线程达到某个屏障,被阻塞,一直到组内最后一个和线程达到屏障时,屏障开放,所有被阻塞的线程会继续运行
CyclicBarrier(int parties,Runnable barrierAction)当屏障开放时,barrierAction里面定义的任务会被执行

CountDownLatch和CyclicBarrier的区别。

1.CountDownLatch是由外部线程控制的决定是否继续运行,CyclicBarrier是由内部自己的线程决定的,只有当自己内部的线程都达到一个屏障时才会放行。
2.CountDownLatch放行让条件>=线程数,CyclicBarrier放行条件=线程数。

Semaphore

控制同时访问某个特定资源的线程数量,用在流量控制

Exchange

两个线程间的数据交换,里面的exchange()方法时阻塞方法,当一个线程先执行到exchange()方法时,会进行等待。等待另一个线程达到exchange()方法

Callable和Runnable区别

Callable 有返回值,能抛出异常。Runnable则不行

Callable、Future和FutureTask

在这里插入图片描述
FutureTask类实现了RunnableFuture接口,能够交给线程跑,里面接收的参数是Callable的实现,能够有返回值。Future接口里面的get()方法阻塞方法,会一直等待Callable运行完,返回结果。get()方法也有一个时间超时的设定。
isDone(),结束,正常还是异常结束,或者自己取消。都返回true。若是还在运行就返回false。
isCancelled() 任务完成前被取消,返回true;
cancel(boolean x):尝试结束任务,参数X:是否对运行中的任务进行中断。
1、任务还没开始,返回false
2、任务已经启动,cancel(true),尝试中断正在运行的任务。中断成功返回true。cancel(false)不会去中断已经运行的任务
3、任务已经结束,返回false

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值