Java核心07-并发包-java.util.concurrent.*

原子操作类(Atomic)

        CAS 操作是进行比较替换,容易出现ABA 问题。从而引入了Atomic 原子类AutomicStampedReference ,使用时间戳版本号来进行标记

  1. 原子基本类型(AtomicInteger,AtomicLong,AtomicBoolean):主要是通过CAS 硬件原语指令实现操作的原子性
  2. 原子数组类型(AtomicIntegerArray,AtomicLongArray,AtomicDoubleArray,AtomicReferenceArray):使用原子方式,更新数组中某个元素。如果数组是以构造方法传递进去,会将当前数组赋值一份,所以,当对内部数组元素进行修改时,不会影响传入的数组) 
  3.  原子引用类型(AtomicReference,AtomicReferenceFiledUpdater,AtomicMarkableReference):原子基本类型只能更新一个变量。而原子引用类型可以更新多个变量。
CountDownLatch
  1. 允许一个或多个线程去等待其它线程完成操作;CountDownLatch接收一个int型参数,表示要等待的工作线程的个数。
  2. CountDownLatch 提供了一些方法:
    1. await() 会阻塞使当前线程进入同步队列进行等待,直到latch的值被减到0或者当前线程被中断,当前线程就会被唤醒。
    2. await(long timeout, TimeUnit unit) 带超时时间的await()。
    3. countDown() 使latch的值减1不会阻塞,会争抢更新volatile 修饰的state字段(类似ReentrantLock) 。如果减到了0,则会唤醒所有等待在这个latch上的线程。
    4. getCount() 获得latch的数值。
  3. 实现原理
    1. CountDownLatch 有一个内部类Sync,继承于AbstractQueuedSynchronizer 类,其中维护了一个被volatile 修饰的int state 字段,用于记录同步状态,从而保证了修改的可见性和原子性。
    2. 执行countDown()方法时,使用CAS方式,去修改state值减1。当减1 后的state值为0时,唤醒被await方法阻塞的所有线程。
    3. 执行awite() 方法时,其实同样采用CAS 方式,是去获取state 的值,当state=0时,跳出循环,执行结束。                
Future&FutureTask
  1. Future:表示一个异步计算的结果。核心包含:get();cancel();isDone();isCancelled()方法获取执行结果状态。当for 循环批量获取Future 的结果时容易block,get 方法调用时应使用timeout 限制。
  2. FutureTask
    1. FutureTask 实现了RunnableFuture 接口,RunnableFuture 接口同时实现了Runnable 接口和Future 接口,所以FutureTask 同时具有Runnable 和Future 的功能。
    2. FutureTask 有2个构造方法,分别传入是Callable 和Runnable ,当传入为Runnable 时,可指定运行结果。若不需要运行结果,则可指定result为 null。
      public FutureTask(Callable<V> callable) {
          if (callable == null)
              throw new NullPointerException();
          this.callable = callable;
          this.state = NEW;       // ensure visibility of callable
      }
      public FutureTask(Runnable runnable, V result) {
          this.callable = Executors.callable(runnable, result);
          this.state = NEW;       // ensure visibility of callable
      }
    3. 可用于异步获取执行结果或取消执行任务的场景。通过传入Runnable 或Callable 的任务给FutureTask,直接调用其run 方法或放入线程池执行,之后可通过FutureTask 的get 方法异步获取执行结果。
    4. 比较适合用于耗时的计算,主线程完成任务后,再去获取结果;
    5. FutureTask 可以确保即使调用多次run 方法,都只会执行一次Runnable 或Calllable 任务。
  3. FutureTask+Thread
    FutureTask task = new FutureTask<>(() -> System.out.print("a"));
    Thread t = new Thread(task);
    t.start();
  4. FutureTask+ExecutorService:多线程提交计算任务,主线程继续执行其它任务。当主线程需要子线程的计算结果时,在异步获取子线程的执行结果。

    FutureTask task = new FutureTask<>(() -> System.out.print("a"));
    ExecutorService es = Executors.newFixedThreadPool(2);
    es.submit(task);
  5. ConcurrentHashMap 和FutureTask 在高并发环境下确保任务只执行一次

    private ConcurrentHashMap<String,FutureTask<Connection>> connectionPool = new ConcurrentHashMap<String, FutureTask<Connection>>();
    
    public Connection getConnection(String key) throws Exception{
        FutureTask<Connection>connectionTask = connectionPool.get(key);
        if(connectionTask != null){
            return connectionTask.get();
        } else {
            Callable<Connection> callable = new Callable<Connection>() {
                @Override
                public Connection call() throws Exception {
                    // TODO Auto-generated method stub
                    return createConnection();
                }
            };
            FutureTask<Connection> newTask =new FutureTask<Connection>(callable);
            connectionTask = connectionPool.putIfAbsent(key, newTask);
            if(connectionTask == null){
                connectionTask = newTask;
                // 执行callable 中call 方法任务,只会有一个线程执行
                connectionTask.run();
            }
            // 阻塞等待
            return connectionTask.get();
        }
    }
    
    //创建Connection
    private Connection createConnection(){
        return null;
    }
CompletableFuture

        其实现了CompletionStage 接口和Future 接口,前者是对后者的一个扩展,增加了异步会点,流式处理,多个Future 组合处理的能力。

        同时实现了对任务编排的能力,可组织不同任务的运行顺序,规则以及方式。类似CountDownLatch。

  • supplyAsync(Supplier<U> supplier, Executor executor):创建带有返回值的异步任务,默认使用ForkJoinPool.commonPool()线程池,可使用自定义的线程池。
    ExecutorService es = Executors.newFixedThreadPool(2);
    CompletableFuture<String> cf = CompletableFuture.supplyAsync(() ->{
        System.out.print("Doing");
        return "success";
    }, es);
    // 或
    CompletableFuture<String> cf = CompletableFuture.supplyAsync(
        new Supplier<String>() {
            @Override
            public String get() {
                System.out.print("Doing");
                return "success";
            }
        }, es);
    
    // 关闭线程池
    es.shutdown();
  • runAysnc(Runnable runnable, Executor executor):创建没有返回值的异步任务。
    ExecutorService es = Executors.newFixedThreadPool(2);
    CompletableFuture<String> cf = CompletableFuture.runAsync(() ->{
        System.out.print("Doing");
    }, es);
    // 或
    CompletableFuture<String> cf = CompletableFuture.runAsync(
        new Runnable() {
            @Override
            public String get() {
                System.out.print("Doing");
                return "success";
            }
        }, es);
    
    // 关闭线程池
    es.shutdown();
  • thenApply和thenApplyAsync:结果转换。表示某个任务执行完成后执行的动作,即回调方法,会将该任务的执行结果即方法返回值作为入参传递到回调方法中,带有返回值。返回的是同一CompletableFuture对象
    ExecutorService es = Executors.newFixedThreadPool(2);
    CompletableFuture<String> cf = CompletableFuture.supplyAsync(() ->{
        System.out.print("Doing");
        return "success";
    }, es).thenApply(res -> {
        return res + ": 1";
    }).thenApply(res -> {
        return res + ": 2";
    });
    
    // 关闭线程池
    es.shutdown();
  • thenCompose: 结果转换。将内部的CompletableFuture 调用展开来并使用上一个CompletableFuture 调用的结果在下一步的CompletableFuture 调用中进行运算,是生成一个新的CompletableFuture
    CompletableFuture<Integer> future = CompletableFuture
        .supplyAsync(new Supplier<Integer>() {
            @Override
            public Integer get() {
                int number = 10;
                System.out.println("第一次运算:" + number);
                return number;
            }
        })
        .thenCompose(new Function<Integer, CompletionStage<Integer>>() {
            @Override
            public CompletionStage<Integer> apply(Integer param) {
                return CompletableFuture.supplyAsync(new Supplier<Integer>() {
                    @Override
                    public Integer get() {
                        int number = param * 2;
                        System.out.println("第二次运算:" + number);
                        return number;
                    }
                });
            }
        });
  • whenComplete 和whenCompleteAsync:结果处理。表示当某个任务执行完成后执行的回调方法,会将执行结果和执行期间抛出的异常传递给回调方法。如果是正常执行则异常为null。回调方法对应的CompletableFuture 的result 和该任务一致,如果该任务正常执行,则get 方法返回执行结果,如果是执行异常,则get 方法抛出异常。
    ExecutorService es = Executors.newFixedThreadPool(2);
    CompletableFuture<String> cf = CompletableFuture.supplyAsync(() ->{
        System.out.print("Doing");
        return "success";
    }, es).whenCompleteAsync((res, throwable) -> {
        if (null == throwable || StrUtil.isBlank(res))  throw new IllegalArgumentException();
        // do something
        
    }, es);
  • thenAccept,thenAcceptBoth,thenRun:结果消费。只对结果执行Action,不返回新的计算值。
    CompletableFuture<Void> future = CompletableFuture
        .supplyAsync(() -> {
            int number = new Random().nextInt(10);
            System.out.println("第一次运算:" + number);
            return number;
        }).thenAccept(number ->
                      System.out.println("第二次运算:" + number * 5));
  • thenCombine,thenAcceptBoth 和runAfterBoth:结果组合。
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread().getName() + " cf1 do something....");
        return 1;
    });
    CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread().getName() + " cf2 do something....");
        return 2;
    });
    
    CompletableFuture<Integer> cf3 = cf1.thenCombine(cf2,(a,b) ->{
       return a+b;
    });
    都是将两个CompletableFuture 组合处理,只有两个任务都正常完成时,才进行下阶段任务。
    • 区别:thenCombine 将两个任务的执行结果作为回调函数参数,且有返回值。
    • thenAcceptBoth 将两个任务的执行结果作为回调函数参数,且无返回值。
    • runAfterBoth:没有入参,也没有返回值。执行下一步操作。
    • 注意:两个任务中只要有一个执行异常,则将该异常信息作为指定任务的执行结果。
  • applyToEither,acceptEither,runAfterEither:任务交互,指将两个线程任务获取结果的速度相比较,按一定的规则进行下一步处理。
    CompletableFuture<Integer> cf1 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread().getName() + " cf1 do something....");
        return 1;
    });
    CompletableFuture<Integer> cf2 = CompletableFuture.supplyAsync(() -> {
        System.out.println(Thread.currentThread().getName() + " cf2 do something....");
        return 2;
    });
    
    CompletableFuture<Integer> cf3 = cf1.applyToEither(cf2, s -> s);
    • 区别:applyToEither 两个任务哪个执行的快,就使用(Function)哪一个结果,有返回值;
    • acceptEither:两个任务哪个执行的快,就消费(Consumer)哪一个结果,无返回值;
    • runAfterEither:任意一个任务执行完,就进行下一步操作(Runnable类型任务)
  • join,get:用来获取CompletableFuture 异步之后的返回值。join 方法抛出的是uncheck 异常,get 方法抛出的经过检查异常,需手动try-catch或抛出处理。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值