Java多线程并发-线程池-CompletableFuture

线程篇

个人blog地址,欢迎来访

线程作用: 提高系统性能,提高响应时间

线程状态

NEW: 新建状态,线程被创建出来,但尚未启动时的线程状态;

RUNNABLE: 就绪状态,表示可以运行的线程状态,它可能正在运行,或者是在排队等待操作系统给它分配 CPU 资源;

BLOCKED: 阻塞等待锁的线程状态,表示处于阻塞状态的线程正在等待监视器锁,比如等待执行 synchronized 代码块或者使用 synchronized 标记的方法;

WAITING: 等待状态,一个处于等待状态的线程正在等待另一个线程执行某个特定的动作,比如,一个线程调用了 Object.wait() 方法,那它就在等待另一个线程调用 Object.notify() 或 Object.notifyAll() 方法;

TIMED_WAITING: 计时等待状态,和等待状态(WAITING)类似,它只是多了超时时间,比如调用了有超时时间设置的方法 Object.wait(long timeout) 和 Thread.join(long timeout) 等这些方法时,它才会进入此状态;

TERMINATED: 终止状态,表示线程已经执行完成

线程工作模式

线程的工作模式是,首先先要创建线程并指定线程需要执行的业务方法,然后再调用线程的 start() 方法,此时线程就从 NEW(新建)状态变成了 RUNNABLE(就绪)状态,此时线程会判断要执行的方法中有没有 synchronized 同步代码块,如果有并且其他线程也在使用此锁,那么线程就会变为 BLOCKED(阻塞等待)状态,当其他线程使用完此锁之后,线程会继续执行剩余的方法。

当遇到 Object.wait() 或 Thread.join() 方法时,线程会变为 WAITING(等待状态)状态,如果是带了超时时间的等待方法,那么线程会进入 TIMED_WAITING(计时等待)状态,当有其他线程执行了 notify() 或 notifyAll() 方法之后,线程被唤醒继续执行剩余的业务方法,直到方法执行完成为止,此时整个线程的流程就执行完了,执行流程如下图所示:

Java多线程并发-线程池-CompletableFuture_第1张图片

代码实例

        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    System.out.println("Thread --- 1");
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
//        t1.join();
        t1.start();

线程池

new线程弊端
  1. 每次new Thread新建对象性能差。
  2. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
  3. 缺乏更多功能,如定时执行、定期执行、线程中断。
线程池优点
  1. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
  2. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
  3. 提供定时执行、定期执行、单线程、并发数控制等功能。
新建线程池
ExecutorService threadPoolExecutor = new ThreadPoolExecutor(4, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(300));
参数介绍

corePoolSize: 核心线程数
maximumPoolSize: 最大线程数
keepAliveTime: 线程存活时间
unit: 存活时间单位
workQueue: 线程队列
threadFactory: 线程工厂
rejectedExecutionHandler: reject策略

线程池队列介绍

当线程池收到任务时,会首先启用核心线程,当核心线程饱和时,会将待执行的任务放入到队列中,当线程池队列满时,线程池会执行reject策略。

四种线程池介绍
newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。

ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
newFixedThreadPool

创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
newScheduledThreadPool

创建一个定长线程池,支持定时及周期性任务执行

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
    @Override
    public void run() {
        System.out.println("delay 3 seconds");
    }
}, 3, TimeUnit.SECONDS);

schedule:指定时间后只执行一次
scheduleAtFixedRate:以固定频率来执行某项任务
scheduleWithFixedDelay:执行某项任务,当任务执行完后,延迟固定时间后,在去执行这项任务

JDK8并发编程

CompletableFuture

CompletableFuture实现了CompletionStage接口和Future接口,前者是对后者的一个扩展,增加了异步回调、流式处理、多个Future组合处理的能力,使Java在处理多任务的协同工作时更加顺畅便利

基础使用
CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
            return "hello world";
  });
System.out.println(future.get());  //阻塞的获取结果  ''helllo world"
get 获取结果
//同步获取结果
public T    get()
public T    get(long timeout, TimeUnit unit)
public T    getNow(T valueIfAbsent)
public T    join()

getNow有点特殊,如果结果已经计算完则返回结果或者抛出异常,否则返回给定的valueIfAbsent值。
**join()get()区别在于join()返回计算的结果或者抛出一个unchecked异常(CompletionException),而get()**返回一个具体的异常
**get()**可以指定超时时间

whenComplete
public CompletableFuture<T>     whenComplete(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T>     whenCompleteAsync(BiConsumer<? super T,? super Throwable> action)
public CompletableFuture<T>     whenCompleteAsync(BiConsumer<? super T,? super Throwable> action, Executor executor)
public CompletableFuture<T>     exceptionally(Function<Throwable,? extends T> fn)

上面4个方法是当计算阶段结束的时候触发,BiConsumer有两个入参,分别代表计算返回值,另外一个是异常.无返回值.方法不以Async结尾,意味着Action使用相同的线程执行,而Async可能会使用其它的线程去执行(如果使用相同的线程池,也可能会被同一个线程选中执行)。
示例

CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
            return "hello world";
  });
future.whenCompleteAsync((v,e)->{
       System.out.println("return value:"+v+"  exception:"+e);
 });
thenApply

当计算结算完成之后,后面可以接继续一系列的thenApply,来完成值的转化

public <U> CompletableFuture<U>     thenApply(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U>     thenApplyAsync(Function<? super T,? extends U> fn)
public <U> CompletableFuture<U>     thenApplyAsync(Function<? super T,? extends U> fn, Executor executor)

示例

CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{      
            return "hello world";
        });

CompletableFuture<String> future3 = future.thenApply((element)->{
            return element+"  addPart";
        }).thenApply((element)->{
            return element+"  addTwoPart";
        });
System.out.println(future3.get());//hello world  addPart  addTwoPart
thenAccept

只对CompletableFuture的结果进行消费,无返回值,也就是最后的CompletableFuture是void

public CompletableFuture<Void>  thenAccept(Consumer<? super T> action)
public CompletableFuture<Void>  thenAcceptAsync(Consumer<? super T> action)
public CompletableFuture<Void>  thenAcceptAsync(Consumer<? super T> action, Executor executor)

示例

CompletableFuture future4 = future.thenAccept((e)->{
            System.out.println("without return value");
        });
future4.join();
thenAcceptBoth

这个方法用来组合两个CompletableFuture,其中一个CompletableFuture等待另一个CompletableFuture的结果.

CompletableFuture<String> future = CompletableFuture.supplyAsync(()->{
            return "hello world";
        });
CompletableFuture future5 =  future.thenAcceptBoth(CompletableFuture.completedFuture("compose"),
                (x, y) -> System.out.println(x+y));//hello world compose

你可能感兴趣的:(java,java,后端,面试,开发语言,spring,boot)