谷粒商城--异步&线程池 --高级篇笔记七

谷粒商城–异步&线程池 --高级篇笔记七

0.测试代码地址

ThreadTest

1. 初始化线程的4种方式

1.1 继承 Thread

package site.zhourui.gilimall.search.thread;

/**
 * @author zr
 * @date 2021/11/22 22:46
 */
public class ThreadTest {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("main......start...");
        Thread01 thread01 = new Thread01();
        thread01.start();
        System.out.println("main......end...");
    }

    public static class Thread01 extends Thread{
    
    
        @Override
        public void run() {
    
    
            System.out.println("当前线程:"+Thread.currentThread().getName());
            Integer i=10/2;
            System.out.println("运行结果:"+i);
        }
    }
}

运行结果

image-20211122225513596

1.2 实现 Runnable 接口

package site.zhourui.gilimall.search.thread;

/**
 * @author zr
 * @date 2021/11/22 22:46
 */
public class ThreadTest {
    
    
    public static void main(String[] args) {
    
    
        System.out.println("main......start...");
//        Thread01 thread01 = new Thread01();
//        thread01.start();

        Thread02 thread02 = new Thread02();
        new Thread(thread02).start();
        System.out.println("main......end...");
    }

    public static class Thread01 extends Thread{
    
    
        @Override
        public void run() {
    
    
            System.out.println("当前线程:"+Thread.currentThread().getName());
            Integer i=10/2;
            System.out.println("运行结果:"+i);
        }
    }

    public static class Thread02 implements Runnable{
    
    
        @Override
        public void run() {
    
    
            System.out.println("当前线程:"+Thread.currentThread().getName());
            Integer i=12/2;
            System.out.println("运行结果:"+i);
        }
    }
}

image-20211122225922053

1.3 实现 Callable 接口 + FutureTask (可以拿到返回结果, 可以处理异常)

package site.zhourui.gilimall.search.thread;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

/**
 * @author zr
 * @date 2021/11/22 22:46
 */
public class ThreadTest {
    
    
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        System.out.println("main......start...");
//        Thread01 thread01 = new Thread01();
//        thread01.start();

//        Thread02 thread02 = new Thread02();
//        new Thread(thread02).start();

        FutureTask futureTask = new FutureTask<>(new Thread03());
        new Thread(futureTask).start();
        Integer i = (Integer) futureTask.get();
        System.out.println("main......end..."+i);
    }

    public static class Thread01 extends Thread{
    
    
        @Override
        public void run() {
    
    
            System.out.println("当前线程:"+Thread.currentThread().getName());
            Integer i=10/2;
            System.out.println("运行结果:"+i);
        }
    }

    public static class Thread02 implements Runnable{
    
    
        @Override
        public void run() {
    
    
            System.out.println("当前线程:"+Thread.currentThread().getName());
            Integer i=12/2;
            System.out.println("运行结果:"+i);
        }
    }

    public static class Thread03 implements Callable {
    
    

        @Override
        public Object call() throws Exception {
    
    
            System.out.println("当前线程:"+Thread.currentThread().getName());
            Integer i=14/2;
            System.out.println("运行结果:"+i);
            return i;
        }
    }
}

image-20211122230756459

1.3.1 注意

实现callable的方法可以拿到返回值

FutureTask继承了Runnable

image-20211122231218541

1.4 线程池

给线程池直接提交任务

1.4.1 初始化线程池(方式一)

创建一个固定类型的线程池

ExecutorService executorService = Executors.newFixedThreadPool(10);

1.4.2 初始化线程池(方式二)

自定义线程池:

扫描二维码关注公众号,回复: 13553285 查看本文章

new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit unit,workQueue, threadFactory, handler);

ThreadPoolExecutor executor = new ThreadPoolExecutor( 
    			 5,
                 200,
                 10,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(  100000),
        Executors.defaultThreadFactory(),
        new ThreadPoolExecutor.AbortPolicy());
1.4.2.1 线程池七大参数
  • corePoolSize:核心线程数,一直存在,一开始只是new 并没有start
  • maximumPoolSize:最大线程数量,控制资源
  • keepAliveime: 【maximumPoolSize-corePoolSize 超过空闲时间释放线程】
  • TimeUnitunit:时间单位
  • workQueue: 阻塞队列,只要有线程空闲,就会去队列取出新的任务执行
    • new LinkedBlockingDeque()默认是Integer的最大值
  • threadFactory:线程的创建工厂【可以自定义】
    • Executors.defaultThreadFactory(),
  • RejectedExecutionHandler handler:拒绝策略
    • 1、丢弃最老的 Rejected
    • 2、调用者同步调用,直接调用run方法,不创建线程了 Caller
    • 3、直接丢弃新任务 Abort 【默认使用这个】
    • 4、丢弃新任务,并且抛出异常 Discard
1.4.2.2 参数之间的关系

1、先创建核心线程运行任务
2、核心线程满放入阻塞队列
new LinkedBlockingDeque()默认是Integer的最大值,
3、阻塞队列满了继续创建线程,最多创建maximumPoolSize个
4、如果传入了拒绝策略会执行,否则抛出异常
5、拒绝策略:
1、丢弃最老的 Rejected
2、调用者同步调用,直接调用run方法,不创建线程了 Caller
3、直接丢弃新任务 Abort 【默认使用这个】
4、丢弃新任务,并且抛出异常 Discard

image-20211123215536859

1.4.3 常见的4种线程池

1、newCachedThreadPool:核心线程数是0,如果空闲会回收所有线程【缓存线程池】

​ 创建一个可缓存线程池, 如果线程池长度超过处理需要, 可灵活回收空闲线程, 若无可回收, 则新建线程。

2、newFixedThreadPool:核心线程数 = 最大线程数,【不回收】

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

3、newScheduledThreadPool:定时任务线程池,多久之后执行【可提交核心线程数,最大线程数是Integer.Max】

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

4、newSingleThreadPool:核心与最大都只有一个【不回收】,后台从队列中获取任务

​ 创建一个单线程化的线程池, 它只会用唯一的工作线程来执行任务, 保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

1.4.4 使用线程池的好处

1、降低资源的消耗【减少创建销毁线程的开销】
通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗

2、提高响应速度【控制线程个数】
因为线程池中的线程数没有超过线程池的最大上限时,有的线程处于等待分配任务的状态,当任务来时无需创建新的线程就能执行

3、提高线程的可管理性【例如系统中可以创建两个线程池,核心线程池、非核心线程池【短信等】,关闭非核心线程池释放内存资源】
线程池会根据当前系统特点对池内的线程进行优化处理,减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗系统资源,还降低系统的稳定性,使用线程池进行统一分配

1.4.5 线程池execute和submit区别(向线程池提交任务)

execute:参数只能是Runnable,没有返回值
submit:参数可以是Runnable、Callable,返回值是FutureTask

注意:声明的线程池必须是全局的不然起不到效果

1.4.6 线程池面试题

面试:
一个线程池 core 7; max 20 , queue: 50, 100 并发进来怎么分配的;
先有 7 个能直接得到执行, 接下来 50 个进入队列排队, 在多开 13 个继续执行。 现在 70 个被安排上了。 剩下 30 个默认拒绝策略。

1.5 以上四种方式的区别

区别;
1、2不能得到返回值。3可以获取返回值
1、2、3都不能控制资源
4可以控制资源,性能稳定,不会一下子所有线程一起运行

1、实际开发中,只用线程池【高并发状态开启了n个线程,会耗尽资源】
2、当前系统中线程池只有一两个,每个异步任务提交给线程池让他自己去执行

2. 异步编排 CompletableFuture

在 Java 8 中, 新增加了一个包含 50 个方法左右的类: CompletableFuture, 提供了非常强大的Future 的扩展功能, 可以帮助我们简化异步编程的复杂性, 提供了函数式编程的能力, 可以通过回调的方式处理计算结果, 并且提供了转换和组合 CompletableFuture 的方法。CompletableFuture 类实现了 Future 接口, 所以你还是可以像以前一样通过get方法阻塞或者轮询的方式获得结果, 但是这种方式不推荐使用。

CompletableFuture 和 FutureTask 同属于 Future 接口的实现类, 都可以获取线程的执行结果。

image-20211123223637995

image-20211123222945095

2.1 创建异步对象 runAsync|supplyAsync

CompletableFuture 提供了四个静态方法来创建一个异步操作。

  1. public static Completab1eFuture runAsync(Runnable runnable)
  2. public static completableFuturecVoid> runAsync(Runnable runnable,Executor executor)
  3. public static CompletableFuture supplyAsync(Suppliersupplier)
  4. public static CompletableFuturecU> supplyAsync(Supplier supplier,Executor executor)

1、 runXxxx 都是没有返回结果的, supplyXxx 都是可以获取返回结果的

2、 可以传入自定义的线程池, 否则就用默认的线程池;

3、Async代表异步方法

2.1.1 runAsync 不带返回值

public class ThreadTest {
    
    
    //        ExecutorService executorService = Executors.newFixedThreadPool(10);
    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(  5,
            200,
            10,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(  100000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());


    public static void main(String[] args) {
    
    
        CompletableFuture<Void> voidCompletableFuture = CompletableFuture.runAsync(() -> {
    
    
            System.out.println("当前线程:"+Thread.currentThread().getName());
            int i = 10 / 2;
            System.out.println("运行结果...."+i);
        }, executor);
    }
}

image-20211124102207850

2.1.2 supplyAsync 带返回值

public class ThreadTest {
    
    
    //        ExecutorService executorService = Executors.newFixedThreadPool(10);
    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(  5,
            200,
            10,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(  100000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());


    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    

        CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getName());
            int i = 12 / 2;
            System.out.println("运行结果...." + i);
            return i;
        }, executor);

        Integer integer = supplyAsync.get();
        System.out.println("返回数据:"+integer);
    }
}

image-20211124141905715

2.2 完成回调whenCompleteAsync与异常感知exceptionally

  1. public completableFuture whencomplete(BiConsumer<? super T,? super Throwable> action);
  2. public CompletableFuturewhenCompleteAsync(BiConsumer <? super T,? super Throwable> action);
  3. public completableFuture whenCompleteAsync(BiConsumer<? super T,? super Throwable> action,Executor executor);
  4. public completableFutureexceptionally(Function<Throwable,? extends T> fn);
  • whenComplete可以处理正常和异常的计算结果,exceptionally处理异常情况。
  • whenComplete 和 whenCompleteAsync 的区别:
    whenComplete: 是执行当前任务的线程执行继续执行 whenComplete 的任务。
    whenCompleteAsync: 是执行把 whenCompleteAsync 这个任务继续提交给线程池
    来进行执行。
  • 方法不以 Async 结尾, 意味着 Action 使用相同的线程执行, 而 Async 可能会使用其他线程执行(如果是使用相同的线程池, 也可能会被同一个线程选中执行)

2.2.1 whenCompleteAsync 完成回调

没有异常情况情况

public class ThreadTest {
    
    
    //        ExecutorService executorService = Executors.newFixedThreadPool(10);
    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(  5,
            200,
            10,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(  100000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());


    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getName());
            int i = 12 / 2;
            System.out.println("运行结果...." + i);
            return i;
        }, executor).whenCompleteAsync((res, exception) -> {
    
    
            System.out.println("异步任务完成....感知到返回值为:"+res+"异常:"+exception);
        },executor);

        Integer integer = supplyAsync.get();
        System.out.println("返回数据:"+integer);
    }
}

image-20211124143329343

有异常情况

public class ThreadTest {
    
    
    //        ExecutorService executorService = Executors.newFixedThreadPool(10);
    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(  5,
            200,
            10,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(  100000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());


    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getName());
            int i = 12 / 0;
            System.out.println("运行结果...." + i);
            return i;
        }, executor).whenCompleteAsync((res, exception) -> {
    
    
            System.out.println("异步任务完成....感知到返回值为:"+res+"异常:"+exception);
        },executor);

        Integer integer = supplyAsync.get();
        System.out.println("返回数据:"+integer);
    }
}

image-20211124143436400

此处虽然得到了异常信息但是没有办法修改返回数据,使用exceptionally自定义异常时的返回值

2.2.2 exceptionally 异常感知及处理

异常情况

public class ThreadTest {
    
    
    //        ExecutorService executorService = Executors.newFixedThreadPool(10);
    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(  5,
            200,
            10,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(  100000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());


    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getName());
            int i = 12 / 0;
            System.out.println("运行结果...." + i);
            return i;
        }, executor).whenCompleteAsync((res, exception) -> {
    
    
            System.out.println("异步任务完成....感知到返回值为:"+res+"异常:"+exception);
        },executor).exceptionally(throwable -> {
    
    
            return 0;
        });

        Integer integer = supplyAsync.get();
        System.out.println("返回数据:"+integer);
    }
}

image-20211124144234343

无异常,情况正常返回不会进exceptionally

public class ThreadTest {
    
    
    //        ExecutorService executorService = Executors.newFixedThreadPool(10);
    public static ThreadPoolExecutor executor = new ThreadPoolExecutor(  5,
            200,
            10,
            TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(  100000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());


    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getName());
            int i = 12 / 2;
            System.out.println("运行结果...." + i);
            return i;
        }, executor).whenCompleteAsync((res, exception) -> {
    
    
            System.out.println("异步任务完成....感知到返回值为:"+res+"异常:"+exception);
        },executor).exceptionally(throwable -> {
    
    
            return 0;
        });

        Integer integer = supplyAsync.get();
        System.out.println("返回数据:"+integer);
    }
}

image-20211124144329253

2.3 最终处理 handle 方法

和 complete 一样, 可对结果做最后的处理(可处理异常),可改变返回值。

总结:使用R apply(T t, U u); 可以感知异常,和修改返回值的功能。

public completionStage handle(BiFunction<? super T,Throwable,? extends U> fn);
public completionStagehandleAsync(BiFunction<? super T,Throwable,? extends U> fn);
public > CompletionStage handleAsync(BiFunction<? super T,Throwable,? extends U> fn,Executor executor ) ;

有异常情况

	public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    

        CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getName());
            int i = 12 / 0;
            System.out.println("运行结果...." + i);
            return i;
        }, executor).handleAsync((res, throwable) -> {
    
    
            if (res!=null){
    
    
                return res*2;
            }
            if (throwable!=null){
    
    
                System.out.println("出现异常"+throwable.getMessage());
                return -1;
            }
            return 0;
        },executor);

        Integer integer = supplyAsync.get();
        System.out.println("返回数据:"+integer);
    }

image-20211124164849976

无异常情况

	public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    

        CompletableFuture<Integer> supplyAsync = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getName());
            int i = 12 / 6;
            System.out.println("运行结果...." + i);
            return i;
        }, executor).handleAsync((res, throwable) -> {
    
    
            if (res!=null){
    
    
                return res*2;
            }
            if (throwable!=null){
    
    
                System.out.println("出现异常"+throwable.getMessage());
                return -1;
            }
            return 0;
        },executor);

        Integer integer = supplyAsync.get();
        System.out.println("返回数据:"+integer);
    }

image-20211124164936759

2.3.1 总结

总结:一般用handle,因为whencomplete如果异常不能给定默认返回结果,需要再调用exceptionally,而handle可以

该方法作用:获得前一任务的返回值【自己也可以是异步执行的】,也可以处理上一任务的异常,调用exceptionally修改前一任务的返回值【例如异常情况时给一个默认返回值】而handle方法可以简化操作

2.4 线程串行化方法

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

public completionstage thenAccept(Consumer<? super T> action);
public completionStage thenAcceptAsync(Consumer<? super T> action);
public CompletionStagecVoid> thenAcceptAsync(Consumer<? super T> action,Executor executor);

public Completionstage thenRun(Runnable action);
public Completionstage thenRunAsync(Runnable action);
public completionStage thenRunAsync(Runnable action,Executor executor);

  • thenApply:继续执行,感知上一任务的返回结果,并且自己的返回结果也被下一个任务所感知
  • thenAccept:继续执行,接受上一个任务的返回结果,自己执行完没有返回结果
  • thenRun:继续执行,不接受上一个任务的返回结果,自己执行完也没有返回结果
  • 以上都要前置任务成功完成。
    Function<? super T,? extends U>
    T: 上一个任务返回结果的类型
    U: 当前任务的返回值类型

2.4.1 thenRunAsync

    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Void>  runAsync= CompletableFuture.runAsync(() -> {
    
    
            System.out.println("当前线程:"+Thread.currentThread().getName());
            int i = 10 / 2;
            System.out.println("运行结果...."+i);
        }, executor).thenRunAsync(() -> {
    
    
            System.out.println("任务二启动了...");
        },executor);

        System.out.println("返回数据:");
    }

image-20211124173536989

2.4.2 thenAcceptAsync

    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Void>  supplyAsync= CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("当前线程:"+Thread.currentThread().getName());
            int i = 10 / 2;
            System.out.println("运行结果...."+i);
            return i;
        }, executor).thenAcceptAsync(res -> {
    
    
            System.out.println("任务二启动了..."+"拿到了上一步的结果:"+res);
        },executor);
        System.out.println("返回数据:");
    }

image-20211124174027613

2.4.3 thenApplyAsync

 public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("当前线程:" + Thread.currentThread().getName());
            int i = 10 / 2;
            System.out.println("运行结果...." + i);
            return i;
        }, executor).thenApplyAsync(res -> {
    
    
            System.out.println("任务二启动了..." + "拿到了上一步的结果:" + res);
            return res*2;
        }, executor);
        Integer integer = future.get();
        System.out.println("返回数据:"+integer);
    }

image-20211124174814204

2.4.4 两任务组合 - 都要完成

  • public <U,V> CompletableFuture thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn);

  • public <U,V> CompletableFuture thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn);

  • public <U,V> CompletableFuture thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor);

  • public CompletableFuture thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action);

  • public CompletableFuture thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action);

  • public CompletableFuture thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action, Executor executor);

  • public CompletableFuture runAfterBoth(CompletionStage<?> other, Runnable action);

  • public CompletableFuture runAfterBothAsync(CompletionStage<?> other, Runnable action);

  • public CompletableFuture runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor);

thenCombine:组合两个future,获取前两个future的返回结果,并返回当前任务的返回值
thenAcceptBoth:组合两个future,获取前两个future任务的返回结果,然后处理任务,没有返回值。
runAfterBoth:组合两个future,不需要获取之前任务future的结果,只需两个future处理完任务后,处理该任务。

2.4.4.1 runAfterBothAsync
 public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务一线程开始:" + Thread.currentThread().getName());
            int i = 12 / 2;
            System.out.println("任务一运行结束...." + i);
            return i;
        }, executor);

        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务二线程开始:" + Thread.currentThread().getName());
            System.out.println("任务二运行结束....");
            return "hello";
        }, executor);

        future01.runAfterBothAsync(future02,() -> {
    
    
            System.out.println("任务三开始...");
        });

        System.out.println("返回数据:");
    }

image-20211124180938009

2.4.4.2 thenAcceptBothAsync
 public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务一线程开始:" + Thread.currentThread().getName());
            int i = 12 / 2;
            System.out.println("任务一运行结束...." + i);
            return i;
        }, executor);

        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务二线程开始:" + Thread.currentThread().getName());
            System.out.println("任务二运行结束....");
            return "hello";
        }, executor);

        future01.thenAcceptBothAsync(future02,(res1, res2) -> {
    
    
            System.out.println("任务一返回值:"+res1+"任务二返回值:"+res2);
        });

        System.out.println("返回数据:");
    }

image-20211124181216559

2.4.4.3 thenCombineAsync
public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    

        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务一线程开始:" + Thread.currentThread().getName());
            int i = 12 / 2;
            System.out.println("任务一运行结束...." + i);
            return i;
        }, executor);

        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务二线程开始:" + Thread.currentThread().getName());
            System.out.println("任务二运行结束....");
            return "hello";
        }, executor);

        CompletableFuture<String> future = future01.thenCombineAsync(future02, (res1, res2) -> {
    
    
            System.out.println("任务一返回值:" + res1 + "任务二返回值:" + res2);

            return res1 + (String) res2;
        }, executor);

        System.out.println("返回数据:"+future.get());
    }

image-20211124182016602

2.4.5 两个任务 - 一个完成

  1. applyToEither: 两个任务有一个执行完成, 获取它的返回值, 处理任务并有新的返回值。
  2. acceptEither: 两个任务有一个执行完成, 获取它的返回值, 处理任务, 没有新的返回值。
  3. runAfterEither: 两个任务有一个执行完成, 不需要获取 future 的结果, 处理任务, 也没有返回值。
public <U> CompletableFuture<U> applyToEither(
    CompletionStage<? extends T> other, Function<? super T, U> fn) {
    
    
    return orApplyStage(null, other, fn);
}

public <U> CompletableFuture<U> applyToEitherAsync(
    CompletionStage<? extends T> other, Function<? super T, U> fn) {
    
    
    return orApplyStage(asyncPool, other, fn);
}

public <U> CompletableFuture<U> applyToEitherAsync(
    CompletionStage<? extends T> other, Function<? super T, U> fn,
    Executor executor) {
    
    
    return orApplyStage(screenExecutor(executor), other, fn);
}

public CompletableFuture<Void> acceptEither(
    CompletionStage<? extends T> other, Consumer<? super T> action) {
    
    
    return orAcceptStage(null, other, action);
}

public CompletableFuture<Void> acceptEitherAsync(
    CompletionStage<? extends T> other, Consumer<? super T> action) {
    
    
    return orAcceptStage(asyncPool, other, action);
}

public CompletableFuture<Void> acceptEitherAsync(
    CompletionStage<? extends T> other, Consumer<? super T> action,
    Executor executor) {
    
    
    return orAcceptStage(screenExecutor(executor), other, action);
}

public CompletableFuture<Void> runAfterEither(CompletionStage<?> other,
                                              Runnable action) {
    
    
    return orRunStage(null, other, action);
}

public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
                                                   Runnable action) {
    
    
    return orRunStage(asyncPool, other, action);
}

public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other,
                                                   Runnable action,
                                                   Executor executor) {
    
    
    return orRunStage(screenExecutor(executor), other, action);
}
2.4.5.1 runAfterEitherAsync
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    

        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务一线程开始:" + Thread.currentThread().getName());
            int i = 12 / 2;
            System.out.println("任务一运行结束...." + i);
            return i;
        }, executor);

        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务二线程开始:" + Thread.currentThread().getName());

            try {
    
    
                Thread.sleep(3000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("任务二运行结束....");
            return "hello";
        }, executor);

        future01.runAfterEitherAsync(future02,() -> {
    
    
            System.out.println("任务三线程开始:" + Thread.currentThread().getName());
        },executor);
        System.out.println("返回数据:");
    }

线程二睡了3秒钟,但是线程一完成了,达成runAfterEitherAsync执行条件

image-20211125213026899

2.4.5.2 acceptEitherAsync
    public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    

        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务一线程开始:" + Thread.currentThread().getName());
            int i = 12 / 2;
            System.out.println("任务一运行结束...." + i);
            return i;
        }, executor);

        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务二线程开始:" + Thread.currentThread().getName());

            try {
    
    
                Thread.sleep(3000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("任务二运行结束....");
            return "hello";
        }, executor);

         future02.acceptEitherAsync(future01,res ->{
    
    
            System.out.println("任务三线程开始:" + Thread.currentThread().getName()+"拿到上次任务的结果:"+res);
        },executor);
        System.out.println("返回数据:");
    }

image-20211125213706303

2.4.5.3 applyToEitherAsync
public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务一线程开始:" + Thread.currentThread().getName());
            int i = 12 / 2;
            System.out.println("任务一运行结束...." + i);
            return i;
        }, executor);

        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务二线程开始:" + Thread.currentThread().getName());

            try {
    
    
                Thread.sleep(3000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("任务二运行结束....");
            return "hello";
        }, executor);

        CompletableFuture<String> future = future02.applyToEitherAsync(future01, res -> {
    
    
            System.out.println("任务三线程开始:" + Thread.currentThread().getName() + "拿到上次任务的结果:" + res);
            return res + "t3";
        }, executor);

        System.out.println("返回数据:"+future.get());
    }

image-20211125214811074

2.4.6 多任务组合

//allOf: 等待所有任务完成
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) {
    
    
    return andTree(cfs, 0, cfs.length - 1);
}

//anyOf: 只要有一个任务完成
public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) {
    
    
    return orTree(cfs, 0, cfs.length - 1);
}
2.4.6.1 allOf
public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务一线程开始:" + Thread.currentThread().getName());
            int i = 12 / 2;
            System.out.println("任务一运行结束...." + i);
            return i;
        }, executor);

        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务二线程开始:" + Thread.currentThread().getName());

            try {
    
    
                Thread.sleep(3000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("任务二运行结束....");
            return "hello";
        }, executor);
        CompletableFuture<Object> future03 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务三线程开始:" + Thread.currentThread().getName());

            try {
    
    
                Thread.sleep(2000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("任务三运行结束....");
            return "hello2";
        }, executor);

        CompletableFuture<Void> allOf = CompletableFuture.allOf(future01, future02, future03);
        allOf.get();//等待所有任务完成
        System.out.println("返回数据:");
    }

image-20211125220104409

2.4.6.2 anyOf
public static void main(String[] args) throws ExecutionException, InterruptedException {
    
    
        CompletableFuture<Integer> future01 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务一线程开始:" + Thread.currentThread().getName());
            int i = 12 / 2;
            System.out.println("任务一运行结束...." + i);
            return i;
        }, executor);

        CompletableFuture<Object> future02 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务二线程开始:" + Thread.currentThread().getName());

            try {
    
    
                Thread.sleep(3000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("任务二运行结束....");
            return "hello";
        }, executor);
        CompletableFuture<Object> future03 = CompletableFuture.supplyAsync(() -> {
    
    
            System.out.println("任务三线程开始:" + Thread.currentThread().getName());

            try {
    
    
                Thread.sleep(2000);
            } catch (InterruptedException e) {
    
    
                e.printStackTrace();
            }
            System.out.println("任务三运行结束....");
            return "hello2";
        }, executor);

         CompletableFuture<Object> anyOf = CompletableFuture.anyOf(future01, future02, future03);
        anyOf.get();//等待其中之一任务完成
        System.out.println("返回数据:");
    }

image-20211125220305434

3. 商品详情搭建

3.1 SwitchHosts增加配置

image-20211125221023973

3.2 nginx配置

按理说之前配置search.gulimall.com时就已经配置了

配置gulimall.conf*.gulimall.com

vim /mydata/nginx/conf/conf.d/gulimall.conf
server_name gulimall.com *.gulimall.com

image-20211125221554742

3.3 网关增加转发规则

因为商品详情也是需要转到商品服务gulimall-product,所以在商品服务的网关上增加item.gulimall.com

- id: gulimall_host
  uri: lb://gulimall-product
  predicates:
    - Host=gulimall.com,item.gulimall.com

3.4 详情页面前端搭建

复制shangpinxiangqing.html到gulimall-product/src/main/resources/templates/目录下,并改名为item.index

老规矩修改srcherf的静态资源地址

image-20211125222702368

将静态资源上传至nginx

image-20211125223131908

3.5 新增ItemController

gulimall-product/src/main/java/site/zhourui/gulimall/product/web/ItemController.java

    @Resource
    private SkuInfoService skuInfoService;
    /**
     * 展示当前sku的详情
     */
    @GetMapping("/{skuId}.html")
    public String skuItem(@PathVariable("skuId") Long skuId, Model model) throws ExecutionException, InterruptedException {
    
    
        System.out.println("准备查询:" + skuId + "详情");

        /**
         * 1、sku基本信息【标题、副标题、价格】pms_sku_info
         * 2、sku图片信息【每个sku_id对应了多个图片】pms_sku_images
         * 3、spu下所有sku销售属性组合【不只是当前sku_id所指定的商品】
         * 4、spu商品介绍【】
         * 5、spu规格与包装【参数信息】
         */
        SkuItemVo vos = skuInfoService.item(skuId);
        model.addAttribute("item",vos);

        return "item";
    }

3.6 线程池抽取配置到配置文件

3.6.1 导入配置文件提示依赖

        <!--自定义配置提示        -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
            <optional>true</optional>
        </dependency>

导入上面依赖才有提示

image-20211128112933843

3.6.2 提取配置文件配置

gulimall-product/src/main/java/site/zhourui/gulimall/product/config/ThreadPoolConfigProperties.java

规定哪些配置可以在配置文件文件中配置

package site.zhourui.gulimall.product.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@ConfigurationProperties(prefix = "gulimall.thread")
@Component
@Data
public class ThreadPoolConfigProperties {
    
    
    private Integer coreSize;
    private Integer maxSize;
    private Integer keepAliveTime;
}

3.6.4 在配置文件中配置

gulimall-product/src/main/resources/application.yml

gulimall:
  thread:
    core-size: 20
    max-size: 200
    keep-alive-time: 10

3.6.5 新增自定义线程池MyThreadConfig

gulimall-product/src/main/java/site/zhourui/gulimall/product/config/MyThreadConfig.java

package site.zhourui.gulimall.product.config;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * @author zr
 * @date 2021/11/28 10:12
 */

@Configuration
public class MyThreadConfig {
    
    
    @Bean
    public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) {
    
    
        return new ThreadPoolExecutor(
                pool.getCoreSize(),
                pool.getMaxSize(),
                pool.getKeepAliveTime(),
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(100000),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );
    }
}

3.6.6 使用自定义线程池

    @Autowired
    private ThreadPoolExecutor executor;


    CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
    
    
        //1、sku基本信息的获取  pms_sku_info
        SkuInfoEntity info = this.getById(skuId);
        skuItemVo.setInfo(info);
        return info;
    }, executor);

3.7 新增item接口,使用线程线程池

gulimall-product/src/main/java/site/zhourui/gulimall/product/service/SkuInfoService.java

    SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException;
    @Autowired
    SkuSaleAttrValueService skuSaleAttrValueService;
    @Autowired
    SpuInfoDescService spuInfoDescService;
    @Autowired
    AttrGroupService attrGroupService;
    @Autowired
    SkuImagesService skuImagesService;

	//注入自定义线程池
    @Autowired
    private ThreadPoolExecutor executor;

	@Override
    public SkuItemVo item(Long skuId) throws ExecutionException, InterruptedException {
    
    

        SkuItemVo skuItemVo = new SkuItemVo();

        CompletableFuture<SkuInfoEntity> infoFuture = CompletableFuture.supplyAsync(() -> {
    
    
            //1、sku基本信息的获取  pms_sku_info
            SkuInfoEntity info = this.getById(skuId);
            skuItemVo.setInfo(info);
            return info;
        }, executor);


        CompletableFuture<Void> saleAttrFuture = infoFuture.thenAcceptAsync((res) -> {
    
    
            //3、获取spu的销售属性组合
            List<SkuItemSaleAttrVo> saleAttrVos = skuSaleAttrValueService.getSaleAttrBySpuId(res.getSpuId());
            skuItemVo.setSaleAttr(saleAttrVos);
        }, executor);


        CompletableFuture<Void> descFuture = infoFuture.thenAcceptAsync((res) -> {
    
    
            //4、获取spu的介绍    pms_spu_info_desc
            SpuInfoDescEntity spuInfoDescEntity = spuInfoDescService.getById(res.getSpuId());
            skuItemVo.setDesc(spuInfoDescEntity);
        }, executor);


        CompletableFuture<Void> baseAttrFuture = infoFuture.thenAcceptAsync((res) -> {
    
    
            //5、获取spu的规格参数信息
            List<SpuItemAttrGroupVo> attrGroupVos = attrGroupService.getAttrGroupWithAttrsBySpuId(res.getSpuId(), res.getCatalogId());
            skuItemVo.setGroupAttrs(attrGroupVos);
        }, executor);

        // Long spuId = info.getSpuId();
        // Long catalogId = info.getCatalogId();

        //2、sku的图片信息    pms_sku_images
        CompletableFuture<Void> imageFuture = CompletableFuture.runAsync(() -> {
    
    
            List<SkuImagesEntity> imagesEntities = skuImagesService.getImagesBySkuId(skuId);
            skuItemVo.setImages(imagesEntities);
        }, executor);

    //等到所有任务都完成
        CompletableFuture.allOf(saleAttrFuture,descFuture,baseAttrFuture,imageFuture).get();
        return skuItemVo;
    }

3.7.1 新增getSaleAttrBySpuId接口

gulimall-product/src/main/java/site/zhourui/gulimall/product/service/SkuSaleAttrValueService.java

    List<SkuItemSaleAttrVo> getSaleAttrBySpuId(Long spuId);
    @Override
    public List<SkuItemSaleAttrVo> getSaleAttrBySpuId(Long spuId) {
    
    

        List<SkuItemSaleAttrVo> saleAttrVos = baseMapper.getSaleAttrBySpuId(spuId);

        return saleAttrVos;
    }

gulimall-product/src/main/java/site/zhourui/gulimall/product/dao/SkuSaleAttrValueDao.java

    List<SkuItemSaleAttrVo> getSaleAttrBySpuId(@Param("spuId") Long spuId);
    List<String> getSkuSaleAttrValuesAsStringList(@Param("skuId") Long skuId);
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="site.zhourui.gulimall.product.dao.SkuSaleAttrValueDao">

    <!-- 可根据自己的需求,是否要使用 -->
    <resultMap type="site.zhourui.gulimall.product.entity.SkuSaleAttrValueEntity" id="skuSaleAttrValueMap">
        <result property="id" column="id"/>
        <result property="skuId" column="sku_id"/>
        <result property="attrId" column="attr_id"/>
        <result property="attrName" column="attr_name"/>
        <result property="attrValue" column="attr_value"/>
        <result property="attrSort" column="attr_sort"/>
    </resultMap>
    <resultMap id="skuItemSaleAttrVo" type="site.zhourui.gulimall.product.vo.SkuItemSaleAttrVo">
        <result column="attr_id" property="attrId"></result>
        <result column="attr_name" property="attrName"></result>
        <collection property="attrValues" ofType="site.zhourui.gulimall.product.vo.AttrValueWithSkuIdVo">
            <result column="attr_value" property="attrValue"></result>
            <result column="sku_ids" property="skuIds"></result>
        </collection>
    </resultMap>
    <select id="getSaleAttrBySpuId" resultMap="skuItemSaleAttrVo">
        SELECT
            ssav.attr_id attr_id,
            ssav.attr_name attr_name,
            ssav.attr_value,
            group_concat( DISTINCT info.sku_id ) sku_ids
        FROM
            pms_sku_info info
                LEFT JOIN pms_sku_sale_attr_value ssav ON ssav.sku_id = info.sku_id
        WHERE
            info.spu_id = #{spuId}
        GROUP BY
            ssav.attr_id,
            ssav.attr_name,
            ssav.attr_value

    </select>

    <select id="getSkuSaleAttrValuesAsStringList" resultType="java.lang.String">
        SELECT
            CONCAT( attr_name, ":", attr_value )
        FROM
            pms_sku_sale_attr_value
        WHERE
            sku_id = #{skuId}
    </select>


</mapper>

3.7.2 新增getAttrGroupWithAttrsBySpuId

gulimall-product/src/main/java/site/zhourui/gulimall/product/service/AttrGroupService.java

    List<SpuItemAttrGroupVo> getAttrGroupWithAttrsBySpuId(Long spuId, Long catalogId);
    @Override
    public List<SpuItemAttrGroupVo> getAttrGroupWithAttrsBySpuId(Long spuId, Long catalogId) {
    
    
        //1、查出当前spu对应的所有属性的分组信息以及当前分组下的所有属性对应的值
        List<SpuItemAttrGroupVo> vos = baseMapper.getAttrGroupWithAttrsBySpuId(spuId,catalogId);
        return vos;
    }

gulimall-product/src/main/java/site/zhourui/gulimall/product/dao/AttrGroupDao.java

    List<SpuItemAttrGroupVo> getAttrGroupWithAttrsBySpuId(Long spuId, Long catalogId);
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="site.zhourui.gulimall.product.dao.AttrGroupDao">

    <!-- 可根据自己的需求,是否要使用 -->
    <resultMap type="site.zhourui.gulimall.product.entity.AttrGroupEntity" id="attrGroupMap">
        <result property="attrGroupId" column="attr_group_id"/>
        <result property="attrGroupName" column="attr_group_name"/>
        <result property="sort" column="sort"/>
        <result property="descript" column="descript"/>
        <result property="icon" column="icon"/>
        <result property="catelogId" column="catelog_id"/>
    </resultMap>
    <!--只要有嵌套属性就要封装自定义结果集-->
    <resultMap id="spuAttrGroup" type="site.zhourui.gulimall.product.vo.SpuItemAttrGroupVo">
        <result property="groupName" column="attr_group_name"/>
        <collection property="attrs" ofType="site.zhourui.gulimall.product.vo.Attr">
            <result property="attrId" column="attr_id"></result>
            <result property="attrName" column="attr_name"></result>
            <result property="attrValue" column="attr_value"></result>
        </collection>
    </resultMap>

    <select id="getAttrGroupWithAttrsBySpuId" resultMap="spuAttrGroup">
        SELECT
            pav.spu_id,
            pag.attr_group_id,
            pag.attr_group_name,
            pav.attr_id,
            pav.attr_name,
            pav.attr_value
        FROM
            pms_product_attr_value pav
                LEFT JOIN pms_attr_attrgroup_relation pagr ON pav.attr_id = pagr.attr_id
                LEFT JOIN pms_attr_group pag ON pagr.attr_group_id = pag.attr_group_id
        WHERE
            pav.spu_id = #{spuId}
          AND pag.catelog_id = #{catalogId}
    </select>


</mapper>

3.7.3 新增getImagesBySkuId接口

gulimall-product/src/main/java/site/zhourui/gulimall/product/service/SkuImagesService.java

    List<SkuImagesEntity> getImagesBySkuId(Long skuId);
    @Override
    public List<SkuImagesEntity> getImagesBySkuId(Long skuId) {
    
    

        List<SkuImagesEntity> imagesEntities = baseMapper.selectList(new QueryWrapper<SkuImagesEntity>().eq("sku_id", skuId));
        return imagesEntities;
    }

3.8 前端代码

前端代码

猜你喜欢

转载自blog.csdn.net/qq_31745863/article/details/121611337
今日推荐