线程并发工具类

  • java.util.concurrent

1.Fork/Join

  • 分而治之
    在这里插入图片描述
  • 工作密取
A线程比B线程先运行完,则从B工作队列的最后取一个任务交给A(AB的忙)
	多线程开发核心:快+资源不能争抢+充分利用资源(尽量让CPU全部跑起来)
  • 使用流程
    在这里插入图片描述
  • ForkJoinTask
--方法------------------------------------
1.fork():将某子任务发生给ForkJoin执行器
	相当于invokeAll(task1,task2...)
2.join():等待某子任务执行结束并返回结果(工作密取)
--子类------------------------------------
1.RecursiveTask:有返回
2.RecursiveActive:无返回
3.CountedCompleter
  • 案例
//forkjoin方式在指定目录(含子目录)寻找指定后缀名的文件并打印
public class ForkJoin_FindDirsFiles extends RecursiveAction {
    //当前任务需要搜寻的目录
    private File path;
    public ForkJoin_FindDirsFiles(File path) {this.path = path;}

    @Override //无返回值
    protected void compute() {
        //所有子任务保存在该列表中,一边一次性激活
        List<ForkJoin_FindDirsFiles> subTasks = new ArrayList<>();
        File[] files = path.listFiles();
        if (files != null) {
            for (File file : files) {
                //如果是文件夹,拆分成新子任务
                if (file.isDirectory()) {
                    ForkJoin_FindDirsFiles sonTask = new ForkJoin_FindDirsFiles(file);
                    subTasks.add(sonTask);
                } else {
                    //如果是文件,判断是不是需要找的文件
                    if (file.getAbsolutePath().endsWith("txt")) {
                        System.out.println("文件:" + file.getAbsolutePath());
                    }
                }
            }
            //子任务列表非空
            if (!subTasks.isEmpty()) {
                //调用invokeAll激活所有子任务
                for (ForkJoin_FindDirsFiles subTask : invokeAll(subTasks)) {
                    //join的目的:等待子任务完成
                    subTask.join();
                }
            }
        }
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        try {
            //1.创建线程池
            ForkJoinPool pool = new ForkJoinPool();
            //2.产生第一个子任务
            ForkJoin_FindDirsFiles task = new ForkJoin_FindDirsFiles(new File("c:/"));

            //异步调用,没有返回值
            //pool.execute(task);
            //3.同步调用,有返回值(此处不能使用)
            pool.invoke(task);
            System.out.println("任务运行中");
            
            //主线程睡
            Thread.sleep(1);
            int otherWork = 0;
            for (int i = 0; i < 100; i++) {
                otherWork = otherWork + 1;
            }
            System.out.println("主线程在完成其他工作,此工作结束" + otherWork);

            //阻塞式方法,主线程让步给task线程
            task.join();

            System.out.println("任务结束");
            System.out.println("耗时:" + (System.currentTimeMillis() - start));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

2.CountDownLatch

  • 作用
一组线程等待其他线程工作完后再运行(加强版join)
解决join中无法知道让行线程合适运行完的问题
  • 方法
await():等待
countDown():计数器减1,减到0线程开始运行
  • 构造
CountDownLatch(初始值):计数器countDown的初值
  • 案例
//一组线程等待,模拟并发,使并发线程一起执行
public class CountDownLatch1 {
    public static void main(String[] args) throws InterruptedException {
        final CountDownLatch countDownLatch = new CountDownLatch(1);
        for (int i = 1; i <= 5; i++) {
            new Thread(() -> {
                try {
                    //循环创建的5个线程全部阻塞此处
                    String name = "[" + Thread.currentThread().getName() + "]";
                    System.out.println(name + "阻塞");
                    countDownLatch.await();
                    String str = "[" + Thread.currentThread().getName() + "]";
                    System.out.println(str + "开始执行");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }).start();
        }
        //主线程睡眠
        Thread.sleep(2000);
        System.out.println("主线程在运行");
        //计数器减1,5个线程都运行
        countDownLatch.countDown();
    }
}

3.CyclicBarrier

  • 作用
一组线程达到某屏障,直到组内最后一个线程到达屏障时,屏障放开,所有阻塞的线程全部继续运行
  • 构造与方法
CyclicBarrier(参与者数量):参与的线程都阻塞,直到阻塞的线程数等于参与线程数时,全部阻塞线程继续运行
CyclicBarrier(参与者数量,Runnable):屏障放开,参数2定义的任务被执行一次

await():当等待者数量到达参与者数量就同时解除屏障
  • 案例
public class UseCyclicBarrier {
    //参数:参与线程数量(有这么多线程上来就结束屏障)
    //private static CyclicBarrier barrier = new CyclicBarrier(5);
    //屏障解开后执行另一个任务
    private static CyclicBarrier barrier = new CyclicBarrier(5, new CollectThread());

    public static void main(String[] args) {
        for (int i = 0; i <= 4; i++) {
            Thread thread = new Thread(new SubThread());
            thread.start();
        }
    }

    //屏蔽开放以后的工作
    private static class CollectThread implements Runnable {
        @Override
        public void run() {
            System.out.println("执行醒来后的另一个处理");
        }
    }

    //工作线程
    private static class SubThread implements Runnable {
        @Override
        public void run() {
            Random rd = new Random();
            try {
                if (rd.nextBoolean()) {
                    Thread.sleep(2000 + Thread.currentThread().getId());
                    System.out.println("Thread_" + Thread.currentThread().getId() + "睡眠两秒后开始");
                }
                System.out.println(Thread.currentThread().getId() + "开始等待");
                //=--------------------模拟线程早到与晚到

                //当5个线程全部到达后开始运行
                barrier.await();

                System.out.println("屏障打开,当前Thread_" + Thread.currentThread().getId() + "完成其他业务逻辑");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
  • CountDownLatch与CyclicBarrier区别
1.CountDownLatch由第三方线程放行,CyclicBarrier由自己放行
2.CountDownLatch放行条件>=线程数,CyclicBarrier放行条件=线程数

4.Exchanger

  • 作用
两个线程间数据交换
  • 方法
exchange():当另一个线程没有调用exchange时,程序在此处阻塞
	返回值是交换到的内容
  • 使用场景
转账时发现钱不够时,又要转回来

5.Semaphore

  • 作用
控制同时访问某资源的线程数量(流量控制)
  • 构造方法
Semaphore(初始数量)
  • 方法
acquire():-1
release():+1

6.Callable/FutureTask

  • 继承结构
    在这里插入图片描述
  • 状态图
    在这里插入图片描述
    在这里插入图片描述
  • 创建
//Callable/FutureTask----->创建3+获取结果3+cancel
public class UseFuture {
    //有返回值所以有泛型约定
    private static class UseCallable implements Callable<Integer> {
        private int sum;
        @Override
        public Integer call() throws Exception {
            System.out.println("Callable中call()开始计算");
            Thread.sleep(2000);
            for (int i = 0; i < 5000; i++) {
                sum += i;
            }
            System.out.println("Callable中call()计算结束,结果=" + sum);
            return sum;
        }
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException, TimeoutException {
        UseCallable useCallable = new UseCallable();
        FutureTask<Integer> futureTask = new FutureTask<>(useCallable);

        //方案1:直接使用Thread类包装任务
        new Thread(futureTask).start();

        //方案2:线程池
        //ExecutorService service = Executors.newCachedThreadPool();
        
        //2.1 submit提交任务返回一个创建的线程
        //一次提交一个任务
        //service.submit(futureTask);

        //2.2 invokeAll(集合<Callable>) 一次性激活,将所有任务的返回结果绑定在对应的Future中,以集合的形式返回
        //2.2 这种方法一般不使用,容易内存溢出
        //批量提交任务
        /*List<UseCallable> list = new ArrayList<>();
        list.add(useCallable);
        //一次性激活
        List<Future<Integer>> futures = service.invokeAll(list);
        //批量异步取值get
        for (Future<Integer> f : futures) {
            System.out.println("异步取值方法" + f.get());
        }*/

        //方案3:线程池手工创建
        /*ExecutorService service1 = new ThreadPoolExecutor(2, 4, 3, TimeUnit.SECONDS,
                new ArrayBlockingQueue<Runnable>(10),
                new ThreadPoolExecutor.DiscardOldestPolicy());
        service.submit(futureTask);*/

        Random random = new Random();
        Thread.sleep(1);
        if (random.nextBoolean()) {
            System.out.println("--获取结果--");
            //方案1:阻塞式无参get,直到结束或有异常时返回
            //System.out.println("阻塞无参get获取Callable结果:" + futureTask.get());

            //方案2:阻塞式有参get,直到计时结束返回结果   TimeoutException
            //System.out.println("阻塞有参get获取Callable结果:" + futureTask.get(1, TimeUnit.SECONDS));
            System.out.println("阻塞有参get获取Callable结果:" + futureTask.get(5, TimeUnit.SECONDS));

            System.out.println("阻塞式get()阻塞结束");
        } else {
            System.out.println("中断计算");
            futureTask.cancel(true);
        }

        //线程池运行完毕一定要关闭,因为线程池中的线程不是精灵线程
        //service.shutdown();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值