Java并发工具类和框架(TODO)

题记:书到用时方恨少,钱到月底不够花!
此文是读取一些关于并发工具、并发类的框架(多是入门级别)的文章时,取精去粕之后的总结,不过更多应该算是备忘了。日常工作中,并发工具其实并不常用,往往碰见适用的场景时又偏偏 记不起对应的API了。本文,专治此症。
此外,在此立个flag,凡有读书,必有输出!

一、Semphore&Exchanger

二、CountDownLatch&CyclicBarrier

三、Phaser

四、Executor& ThreadPoolExcecutor

五、Future&Callable

六、CompletionService

七、ExecutorService的API

7.1 API overview

首先来概览一下,这个接口大概有哪些重要API:
在这里插入图片描述

  • invokeAny() 与 invokeAll() 具有阻塞特性
  • invokeAny() 取得第一个完成任务的结果值,当第一个任务执行完成后,调用interrupt() 中断其他任务,所以可以结合if(Thread.intterruptted())决定任务是否继续运行。
  • invokeAll() 等全部线程任务执行完成后,取得全部完成任务的结果值。

7.2 invokeAny

invokeAny、invokeAll 都具有阻塞特性

1、invokeAny(Collection tasks) 与interrupted状态

  • tasks执行完任意一个,才会唤醒主线程。并且会返回 第一个执行完任务的结果。
  • tasks中假设有 t1 ,t2 两个任务,t1快 t2慢,则t1完成时,会给t2 状态设置为 interrupted。可在t2 中增加if(Thread.interrputed)的判断,中断 正在执行的t2线程。(见demo7.2.1)

2、invokeAny(Collection tasks) 与 执行慢的任务异常

假设 tasks 里有t1 、 t2两个任务:

  • a. 设 t1 快,t2 慢,t1正常,t2异常。默认不会在主线程输出异常,要显式在t2中抓异常。即使在t2 中显式地抓住了异常,该异常仍然不会影响到主线程的正常执行,言下之意是,主线程捕捉不到子线程抛出的异常(demo 7.2.2 & demo 7.2.3)

  • b. 设t1 快,t2 慢,t1 异常,t2 正常,则t1 的异常仍然不能显示到控制台,而需要显式try catch,同时会等待慢的任务t2返回结果值。 假如t1 中显式 try catch 了后没有重新将这个异常抛出,则仍将返回 t1 的结果,因为在主线程看来,并没有收到 t1 异常的消息(这个异常被吞掉了)。
    注:先出现异常,但不影响后面任务取值的原理在于源码中一直判断(死循环)有无正确的返回值,若到最后都无返回值则抛出异常,这个异常就是最后出现的异常。参见: AbstractExcecutorService method: doInvokeAny()
    //todo 源码分析

  • c. 设tasks 里有三个任务A、B、C,设A B C 一起执行,都有异常,最终的异常就是最后一个出现的异常。(demo 7.2.4)

总结一下:tasks 任务中假如有正常的,则返回最后一个正常任务的结果;
假如都是异常的,则主线程也会抛出异常。
假如任务中try catch 中显式地 捕获了异常,却没有再次往上抛,则该任务对主线程来说是正常的(相当于异常被“吞掉”了)。

3、invokeAny(Collection tasks, timeout ,timeUnits)

在指定的时间内取得第一个先执行完任务的结果值。假如在指定时间内没能执行完任务,将会抛出异常。超时时,执行中的任务状态会变成“interrupted”,所以可以结合

if(Thread.current().isInterrupted) {
	throw new InterruptionExceptioin()
}

来使运行中的线程通过抛异常中断。

再提出一个具体的场景:假如一个任务在执行时既出现了异常,又超时了,会出现什么现象呢?
答曰:主线程超时后会抛出异常,中断主线程的继续执行,所以不能获取任务的执行结果;主线程抛出异常后,子任务会在执行完毕后被interrupt。不过需要注意的是,如果希望记录子线程的异常,需要主动try catch ,若只是简单throw 出去,是打印不了异常栈轨的。//demo 7.2.5

// ===================下面都是代码demo====================
// demo7.2.1

public class MyExecutorService {

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        List<Callable<String>> list = new ArrayList<>();
        list.add(new MyCallableA());
        list.add(new MyCallableB1());

        ExecutorService executorService = Executors.newFixedThreadPool(10);
        // invokeAny --> 只取最先完成任务的结果值
        // 从结果来看就是:打印出 any 以后,主线程继续运行,而 MyCallableB1 线程继续执行完
        // invokeAny --> 此方法具有阻塞特性,主线程会在此阻塞
        String any = executorService.invokeAny(list);
        System.out.println("invokeAny method gets value " + any);
        System.out.println("main thread ends here!");
    }
}

class MyExecutorService2{
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        List<Callable<String>> list = new ArrayList<>();
        list.add(new MyCallableA());
        list.add(new MyCallableB2());

        ExecutorService executorService = Executors.newFixedThreadPool(10);
        // invokeAny --> 只取最先完成任务的结果值
        // 从结果来看就是:打印出 any 以后,主线程继续运行,而 MyCallableB2  线程继续执行完
        //而由于 MyCallableB2 中会对 当前线程状态做出判断,假如是已标记为“ interrupted” ,就
        // 抛出异常中断,需要注意的是: MyCallableB2 中断后,异常并不会传递到 主线程
        // invokeAny --> 此方法具有阻塞特性,主线程会在此阻塞
        String any = executorService.invokeAny(list);
        System.out.println("invokeAny method gets value " + any);
        System.out.println("main thread ends here!");
    }
}

class MyCallableA implements Callable<String>{

    @Override
    public String call() throws Exception {
        System.out.println("MyCallableA begins " + System.currentTimeMillis());
        IntStream.range(0, 10_00).forEach(e -> {
            Math.random();Math.random();Math.random();
            System.out.println("MyCallableA " + (e + 1));
        });
        System.out.println("MyCallableA ends " + System.currentTimeMillis());
        return " return A ";
    }
}

class MyCallableB1 implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("MyCallableB1 begins " + System.currentTimeMillis());
        IntStream.range(0, 20_00).forEach(e -> {
            Math.random();Math.random();Math.random();
            System.out.println("MyCallableB1 " + (e + 1));
        });
        System.out.println("MyCallableB1 ends " + System.currentTimeMillis());
        return "return B1 ";
    }
}

class MyCallableB2 implements Callable<String> {

    @Override
    public String call() throws Exception {
        System.out.println("MyCallableB2 begins " + System.currentTimeMillis());
        for (int e = 0; e < 300_000; e++) {
            if (!Thread.currentThread().isInterrupted()) {
                Math.random();Math.random();Math.random();
                System.out.println("MyCallableB2 " + (e + 1));
            } else {
                System.out.println(" 抛异常了,中断了!");
                throw new InterruptedException("中断了");
            }
        }
        System.out.println("MyCallableB2 ends " + System.currentTimeMillis());
        return "return B2";
    }
}

// demo 7.2.2
class MyExecutorService3{
    public static void main(String[] args) {
        Callable<String> callable1 = () -> {
            System.out.println("callable1 " + Thread.currentThread().getName() +
                    " begins " + System.currentTimeMillis());
            IntStream.range(0, 1_000).forEach(e -> {
                Math.random();Math.random();Math.random();
                System.out.println(" callable1 running " + (e + 1));
            });
            System.out.println("callable1 " + Thread.currentThread().getName() +
                    " ends " + System.currentTimeMillis());
            return " callable1";
        };

        Callable<String> callable2 = () -> {
            System.out.println("callable2 " + Thread.currentThread().getName() +
                    " begins " + System.currentTimeMillis());
            IntStream.range(0, 1_0000).forEach(e -> {
                Math.random();Math.random();Math.random();Math.random();
                System.out.println(" callable2 running " + (e + 1));
            });
            if (true) {
                // 此处抛出的NPE不会在主线程打印出来。必须显式地 try catch,才能捕捉到异常,打印到控制台
                System.out.println("===开始中断 callable2 ====");
                throw new NullPointerException("被抛出的NPE");
            }
            System.out.println("callable2 " + Thread.currentThread().getName() +
                    " ends " + System.currentTimeMillis());
            return " callable2 ";
        };

        ArrayList<Callable<String>> callables = Lists.newArrayList(callable1, callable2);
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        try {
            String any = executorService.invokeAny(callables);
            System.out.println(" 获取 执行结果值: " + any);
            System.out.println(" 主线程继续执行 ");
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(" main 1 ");
        } catch (ExecutionException e) {
            e.printStackTrace();
            System.out.println(" main 2 ");
        }

    }
}

// demo 7.2.3
class MyExecutorService4{
    public static void main(String[] args) {
        Callable<String> callable1 = () -> {
            System.out.println("callable1 " + Thread.currentThread().getName() +
                    " begins " + System.currentTimeMillis());
            IntStream.range(0, 1_000).forEach(e -> {
                Math.random();Math.random();Math.random();
                System.out.println(" callable1 running " + (e + 1));
            });
            System.out.println("callable1 " + Thread.currentThread().getName() +
                    " ends " + System.currentTimeMillis());
            return " callable1";
        };

        // 显式 try catch ,将错误捕捉到
        Callable<String> callable2 = () -> {
            try {
                System.out.println("callable2 " + Thread.currentThread().getName() +
                        " begins " + System.currentTimeMillis());
                IntStream.range(0, 1_0000).forEach(e -> {
                    Math.random();Math.random();Math.random();Math.random();
                    System.out.println(" callable2 running " + (e + 1));
                });
                if (true) {
                    // 此处抛出的NPE不会在主线程打印出来。必须显式地 try catch,才能捕捉到异常,打印到控制台
                    System.out.println("===开始中断 callable2 ====");
                    throw new NullPointerException("被抛出的NPE");
                }
                System.out.println("callable2 " + Thread.currentThread().getName() +
                        " ends " + System.currentTimeMillis());
            } catch (NullPointerException e) {
                e.printStackTrace();
                // =======捕捉到 异常之后,再主动抛出去=====
                throw e;
            }
            return " callable2 ";
        };

        ArrayList<Callable<String>> callables = Lists.newArrayList(callable1, callable2);
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        try {
            String any = executorService.invokeAny(callables);
            System.out.println(" 获取 执行结果值: " + any);
            System.out.println(" 主线程继续执行 ");
            
            // ====主线程里无法 catch 到 子线程抛出的异常(线程间不直接通信)
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(" main 1 ");
        } catch (ExecutionException e) {
            e.printStackTrace();
            System.out.println(" main 2 ");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(" main 3 ");
        }

    }
}

//demo :7.2.4
class MyExecutorService5 {
    public static void main(String[] args) {
        
        // 快
        Callable<String> callable1 = () -> {
            System.out.println("callable1 " + Thread.currentThread().getName() +
                    " begins " + System.currentTimeMillis());
            IntStream.range(0, 1_000).forEach(e -> {
                Math.random();Math.random();Math.random();
                System.out.println(" callable1 running " + (e + 1));
            });
            if (true) {
                System.out.println(" ==== 开始中断 callable1 === ");
                throw new RuntimeException("被抛出的NPE callable1 ");
            }
            System.out.println("callable1 " + Thread.currentThread().getName() +
                    " ends " + System.currentTimeMillis());
            return " callable1";
        };

        // 慢
        Callable<String> callable2 = () -> {
            System.out.println("callable2 " + Thread.currentThread().getName() +
                    " begins " + System.currentTimeMillis());
            IntStream.range(0, 1_0000).forEach(e -> {
                Math.random();Math.random();Math.random();Math.random();
                System.out.println(" callable2 running " + (e + 1));
            });
            if (true) {
                // 此处抛出的NPE不会在主线程打印出来。必须显式地 try catch,才能捕捉到异常,打印到控制台
                System.out.println("===开始中断 callable2 ====");
                throw new NullPointerException("被抛出的NPE callable2 ");
            }
            System.out.println("callable2 " + Thread.currentThread().getName() +
                    " ends " + System.currentTimeMillis());
            return " callable2 ";
        };

        ArrayList<Callable<String>> callables = Lists.newArrayList(callable1, callable2);
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        try {
            // 由于两个 子任务都抛出了异常,主线程实际上也会抛异常
            String any = executorService.invokeAny(callables);
            System.out.println(" 获取 执行结果值: " + any);
            System.out.println(" 主线程继续执行 ");

        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(" main 1 ");
        } catch (ExecutionException e) {
            e.printStackTrace();
            // 进入了这个方法块!主线程 捕捉到了 子线程的异常!
            System.out.println(" main 2 ");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(" main 3 ");
        }

    }
}

// demo 7.2.5
class MyExecutorService6{
    public static void main(String[] args) {
        Callable<String> callable1 = () -> {
            System.out.println("callable1 " + Thread.currentThread().getName() +
                    " begins " + System.currentTimeMillis());
            IntStream.range(0, 1_234).forEach(e -> {
                Math.random();Math.random();Math.random();Math.random();Math.random();
                System.out.println(" callable1 running " + (e + 1));
            });
            if (true) {
                System.out.println(" ==== 开始中断 callable1 === ");
                // 注: 假如此处不使用 try catch 抓住异常,那么抛出的异常是不会在控制台显示的
                throw new NullPointerException("被抛出的NPE callable1 ");
            }
            System.out.println("callable1 " + Thread.currentThread().getName() +
                    " ends " + System.currentTimeMillis());
            return " callable1";
        };

        ExecutorService executorService = Executors.newFixedThreadPool(10);
        try {
            //
            String any = executorService.invokeAny(Lists.newArrayList(callable1), 10 , TimeUnit.MILLISECONDS);
            System.out.println("获取 任务结果" + any);
            System.out.println("---------");
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println(" main A ");
        } catch (ExecutionException e) {
            e.printStackTrace();
            System.out.println(" main B ");
        } catch (TimeoutException e) {
            // ======主线程会进入本代码块,表示 主线程已经抛出了异常,try 中的任务结果不能再获取了=====
            e.printStackTrace();
            System.out.println("main C");
        }

    }
}

7.3 invokeAll

2、List<Future> invokeAll(Collection tasks)
tasks 执行完所有的,才会唤醒主线程。并且会返回每一个执行完的结果。

八、ScheduledExecutorService API

九、Fork-Join

十、并发集合

十一、总结

猜你喜欢

转载自blog.csdn.net/qq_30118563/article/details/83446511