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