商城项目【尚品汇】08异步编排

文章目录

  • 1.线程的创建方式
    • 1.1继承Thread类,重写run方法
    • 1.2实现Runnable接口,重写run方法。
    • 1.3实现Callable接口,重新call方法
    • 1.4以上三种总结
    • 1.5使用线程池创建线程
      • 1.5.1线程池创建线程的方式
      • 1.5.2线程池的七大参数含义
      • 1.5.3线程池的工作流程
      • 1.5.4一个线程池core:7,max:20,queue:50。100个并发进来,怎么分配。
  • 2.CompletableFuture异步编排
    • 2.1创建异步对象方式
    • 2.2计算完成时回调方法
      • 2.1.1方法完成时的感知(方法一)
      • 2.1.2方法完成时的处理(方法二)
    • 2.3线程的串行化的方法
      • 2.3.1不能接收值且没有返回值
      • 2.3.2可以接收值但是没有返回值
      • 2.3.3可以接收值也可以返回值
    • 2.4两任务组合-一个完成即可
    • 2.5两任务组合-两个都要完成
    • 2.6多任务组合
    • 2.7查看商品详情实战

1.线程的创建方式

1.1继承Thread类,重写run方法

package com.atguigu.gmall.product.thread;import java.math.BigDecimal;public class ThreadTest {public static void main(String[] args) {/*** 线程的创建方式* 1.继承Thread类*///开启线程System.out.println("主线程开始");Thread thread = new Thread01();thread.start();System.out.println("主线程完毕");}public static class Thread01 extends Thread{//创建线程方法一//通过继承Thread类重写run()方法,在run()方法中编写业务类@Overridepublic void run() {System.out.println("通过继承Thread类,重写run()方法,创建线程"+Thread.currentThread().getId());BigDecimal bigDecimal = new BigDecimal(10);BigDecimal bigDecimal1 = new BigDecimal(3);BigDecimal divide = bigDecimal1.divide(bigDecimal);System.out.println("divide = " + divide);}}
}

结果
在这里插入图片描述

1.2实现Runnable接口,重写run方法。

package com.atguigu.gmall.product.thread;import java.math.BigDecimal;public class RunableTest {public static void main(String[] args) {/*** 创建线程的方法二:* 通过实现Runable接口,重新run方法,创建线程。*///开启线程System.out.println("主线程开始");Runable01 runable01 = new Runable01();Thread thread = new Thread(runable01);thread.start();System.out.println("主线程完毕");}public static class Runable01 implements Runnable{@Overridepublic void run() {System.out.println("通过实现Runnable接口,重写run()方法,创建线程"+Thread.currentThread().getId());BigDecimal bigDecimal = new BigDecimal(10);BigDecimal bigDecimal1 = new BigDecimal(3);BigDecimal divide = bigDecimal1.divide(bigDecimal);System.out.println("divide = " + divide);}}
}

在这里插入图片描述

1.3实现Callable接口,重新call方法

package com.atguigu.gmall.product.thread;import java.math.BigDecimal;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;public class CallableTest {public static void main(String[] args) throws ExecutionException, InterruptedException {/*** 创建线程的方法三* 通过实现Callable<>接口,重写call方法,创建线程。可以获取到线程的返回值*/System.out.println("主线程开始");FutureTask<String> futureTask = new FutureTask<String>(new Callable01());//开启线程new Thread(futureTask).start();//获取线程的返回值,会阻塞主线程System.out.println("主线程阻塞。。。。。。");String s = futureTask.get();System.out.println("线程的返回值s = " + s);System.out.println("主线程结束");}public static class Callable01 implements Callable<String>{@Overridepublic String call() throws Exception {System.out.println("通过实现Callable<>接口,重写call方法,创建线程。可以获取到线程的返回值"+Thread.currentThread().getId());BigDecimal bigDecimal = new BigDecimal(10);BigDecimal bigDecimal1 = new BigDecimal(3);BigDecimal divide = bigDecimal1.divide(bigDecimal);System.out.println("divide = " + divide);return divide.toString();}}
}

在这里插入图片描述

1.4以上三种总结

1.开启线程的方式,Thread对象调用start方法。
2.以上三种只有第三种可以接收线程的返回值。

1.5使用线程池创建线程

1.5.1线程池创建线程的方式

        /*** 使用线程池创建线程*/ThreadPoolExecutor executor = new ThreadPoolExecutor(10,20,10,TimeUnit.SECONDS,new ArrayBlockingQueue<>(1000),Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());

1.5.2线程池的七大参数含义

    /*** Creates a new {@code ThreadPoolExecutor} with the given initial* parameters.** @param corePoolSize the number of threads to keep in the pool, even*        if they are idle, unless {@code allowCoreThreadTimeOut} is set* @param maximumPoolSize the maximum number of threads to allow in the*        pool* @param keepAliveTime when the number of threads is greater than*        the core, this is the maximum time that excess idle threads*        will wait for new tasks before terminating.* @param unit the time unit for the {@code keepAliveTime} argument* @param workQueue the queue to use for holding tasks before they are*        executed.  This queue will hold only the {@code Runnable}*        tasks submitted by the {@code execute} method.* @param threadFactory the factory to use when the executor*        creates a new thread* @param handler the handler to use when execution is blocked*        because the thread bounds and queue capacities are reached*/public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler)
  • corePoolSize:核心的线程池数。也就是线程池一创建就有的。
  • maximumPoolSize:最大的线程池数。这个线程池可以创建的最大的线程池数。
  • keepAliveTime:当线程池中的线程数大于核心的线程池时,这些线程池执行完任务保持存活的时间。
  • unit:时间单位
  • workQueue:阻塞队列,当任务大于核心线程数时,任务就会放在阻塞队列中。
  • threadFactory:创建工厂。指定线程名。
  • handler:拒绝策略。当线程池中所有的线程都在执行任务,而且阻塞队列已经满了。那么来了任务就需要执行拒绝策略了。

1.5.3线程池的工作流程

1、创建线程池,会创建core线程。
2、当任务来了,core线程进行处理,若core不够,那么就会将任务放在workQueue中,当核心线程空闲下来,去workQueue阻塞队列中去任务。
3、若阻塞队列满了,线程池就去开启新的线程,直至线程池中的线程数达到maximumPoolSize最大线程池数。若新的线程空闲下来,过了过期时间,就会自动销毁。
4、若线程池中的线程池数达到了最大线程池数,而且还来了任务,那么就会使用拒绝策略进行处理。
5、所有的线程都是由指定的factory工厂创建的。

1.5.4一个线程池core:7,max:20,queue:50。100个并发进来,怎么分配。

首先:7个线程直接进行处理。
然后:进入队列50个。
再次:开启13个线程进行处理。
最后:70个被安排,30个交给阻塞队列。

2.CompletableFuture异步编排

2.1创建异步对象方式

   //方法一:public static CompletableFuture<Void> runAsync(Runnable runnable) {return asyncRunStage(asyncPool, runnable);}//方法二public static CompletableFuture<Void> runAsync(Runnable runnable,Executor executor) {return asyncRunStage(screenExecutor(executor), runnable);}//方法三public static <U> CompletableFuture<U> supplyAsync(Supplier<U>supplier) {return asyncSupplyStage(asyncPool, supplier);}//方法四public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier,Executor executor) {return asyncSupplyStage(screenExecutor(executor), supplier);}

1.runXxx方法没有返回值,supplyXxx方法有返回值。
2.可以传入自定义的线程池,否则默认的线程池。
3.都不会接收返回值。

代码

package com.atguigu.gmall.product.completableFuture;import rx.Completable;import java.math.BigDecimal;
import java.util.concurrent.*;public class Test {public static ExecutorService executors = Executors.newFixedThreadPool(10);public static void main(String[] args) throws ExecutionException, InterruptedException {/*** 1.创建异步对象*///CompletableFuture类中的静态方法long startMain = System.currentTimeMillis();System.out.println("主线程--开始");CompletableFuture<Void> future01 = CompletableFuture.runAsync(new Runnable01());CompletableFuture<Void> future02 = CompletableFuture.runAsync(() -> {long start02 = System.currentTimeMillis();System.out.println("id============================");long id = Thread.currentThread().getId();System.out.println("当前线程的id = " + id);BigDecimal a = new BigDecimal(10);BigDecimal b = new BigDecimal(2);BigDecimal divide = a.divide(b);System.out.println("运行结果divide = " + divide+"02-"+(System.currentTimeMillis() - start02));}, executors);CompletableFuture<String> future03 = CompletableFuture.supplyAsync(() -> {long start03 = System.currentTimeMillis();long id = Thread.currentThread().getId();System.out.println("id============================");System.out.println("当前线程的id = " + id);BigDecimal a = new BigDecimal(10);BigDecimal b = new BigDecimal(2);BigDecimal divide = a.divide(b);System.out.println("运行结果divide = " + divide+"03-"+(System.currentTimeMillis() - start03));return divide.toString();});System.out.println("获取返回结果future03.get() = " + future03.get());CompletableFuture<String> future04 = CompletableFuture.supplyAsync(() -> {long start04 = System.currentTimeMillis();long id = Thread.currentThread().getId();System.out.println("id============================");System.out.println("当前线程的id = " + id);BigDecimal a = new BigDecimal(10);BigDecimal b = new BigDecimal(2);BigDecimal divide = a.divide(b);System.out.println("运行结果divide = " + divide+"04-"+(System.currentTimeMillis() - start04));return divide.toString();},executors);System.out.println("获取返回结果future04 = " + future04.get());System.out.println("主线程--结束"+"Main用时"+(System.currentTimeMillis() - startMain));}public static class Runnable01 implements Runnable{@Overridepublic void run() {long start01 = System.currentTimeMillis();System.out.println("id============================");long id = Thread.currentThread().getId();System.out.println("当前线程的id = " + id);BigDecimal a = new BigDecimal(10);BigDecimal b = new BigDecimal(2);BigDecimal divide = a.divide(b);System.out.println("运行结果divide = " + divide+"01-"+(System.currentTimeMillis() - start01));}}public static class Callable01 implements Callable<String> {@Overridepublic String call() {System.out.println("id============================");long id = Thread.currentThread().getId();System.out.println("当前线程的id = " + id);BigDecimal a = new BigDecimal(10);BigDecimal b = new BigDecimal(2);BigDecimal divide = a.divide(b);System.out.println("运行结果divide = " + divide);return divide.toString();}}
}

2.2计算完成时回调方法

2.1.1方法完成时的感知(方法一)

    public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action) {return uniWhenCompleteStage(null, action);}public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action) {return uniWhenCompleteStage(asyncPool, action);}public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor) {return uniWhenCompleteStage(screenExecutor(executor), action);}public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn) {return uniExceptionallyStage(fn);}

whenComplete 可以处理正常结果但是不能返回结果、感知异常但是不能处理异常。这个方法不可以进行返回值
exceptionally可以感知异常并且修改返回值进行返回。

whenComplete 和 whenCompleteAsync 的区别:
whenComplete:是执行当前任务的线程执行继续执行 whenComplete 的任务。
whenCompleteAsync:是执行把 whenCompleteAsync 这个任务继续提交给线程池来进行执行。
方法不以 Async 结尾,意味着 Action 使用相同的线程执行,而 Async 可能会使用其他线程执行(如果是使用相同的线程池,也可能会被同一个线程选中执行)
代码示例

package com.atguigu.gmall.product.completableFuture;import java.math.BigDecimal;
import java.util.concurrent.*;public class Test02 {public static ExecutorService executors = Executors.newFixedThreadPool(10);public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> exceptionally = CompletableFuture.supplyAsync(() -> {int i = 10/0;return "a";}).whenCompleteAsync((res, exception) -> {//尽可以感到异常,不可以修改返回结果System.out.println("输出返回结果" + res);}, executors).exceptionally((exception -> {//可以感到异常,并且修改返回结果return "b";}));System.out.println("获取返回结果:" + exceptionally.get());}public static class Runnable01 implements Runnable{@Overridepublic void run() {long start01 = System.currentTimeMillis();System.out.println("id============================");long id = Thread.currentThread().getId();System.out.println("当前线程的id = " + id);BigDecimal a = new BigDecimal(10);BigDecimal b = new BigDecimal(2);BigDecimal divide = a.divide(b);System.out.println("运行结果divide = " + divide+"01-"+(System.currentTimeMillis() - start01));}}public static class Callable01 implements Callable<String> {@Overridepublic String call() {System.out.println("id============================");long id = Thread.currentThread().getId();System.out.println("当前线程的id = " + id);BigDecimal a = new BigDecimal(10);BigDecimal b = new BigDecimal(2);BigDecimal divide = a.divide(b);System.out.println("运行结果divide = " + divide);return divide.toString();}}
}

2.1.2方法完成时的处理(方法二)

    public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) {return uniHandleStage(null, fn);}public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) {return uniHandleStage(asyncPool, fn);}public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) {return uniHandleStage(screenExecutor(executor), fn);}

不仅可以处理正常结果而且可以处理异常
不仅可以接收值,而且可以返回处理结果

代码实例

package com.atguigu.gmall.product.completableFuture;import java.math.BigDecimal;
import java.util.concurrent.*;public class Test02 {public static ExecutorService executors = Executors.newFixedThreadPool(10);public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<String> exceptionally = CompletableFuture.supplyAsync(() -> {int i = 10/0;return "a";}).handleAsync((res,exception) -> {//不仅可以接收参数,而且可以返回结果if (res != null){return "值"+res;}if (exception != null){return "异常"+exception.getMessage();}return "0";},executors);System.out.println("获取返回结果:" + exceptionally.get());}public static class Runnable01 implements Runnable{@Overridepublic void run() {long start01 = System.currentTimeMillis();System.out.println("id============================");long id = Thread.currentThread().getId();System.out.println("当前线程的id = " + id);BigDecimal a = new BigDecimal(10);BigDecimal b = new BigDecimal(2);BigDecimal divide = a.divide(b);System.out.println("运行结果divide = " + divide+"01-"+(System.currentTimeMillis() - start01));}}public static class Callable01 implements Callable<String> {@Overridepublic String call() {System.out.println("id============================");long id = Thread.currentThread().getId();System.out.println("当前线程的id = " + id);BigDecimal a = new BigDecimal(10);BigDecimal b = new BigDecimal(2);BigDecimal divide = a.divide(b);System.out.println("运行结果divide = " + divide);return divide.toString();}}
}

2.3线程的串行化的方法

2.3.1不能接收值且没有返回值

thenRun方法:只要上面的任务执行完成,就开始执行thenRun,只是处理完任务后,执行 thenRun的后续操作

    public CompletableFuture<Void> thenRun(Runnable action) {return uniRunStage(null, action);}public CompletableFuture<Void> thenRunAsync(Runnable action) {return uniRunStage(asyncPool, action);}public CompletableFuture<Void> thenRunAsync(Runnable action,Executor executor) {return uniRunStage(screenExecutor(executor), action);}

代码示例

package com.atguigu.gmall.product.completableFuture;import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Test03 {public static ExecutorService excutor =Executors.newFixedThreadPool(10);public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<Void> future01 = CompletableFuture.supplyAsync(() -> {int i = 0;System.out.println("i = " + i);return i;}).thenRunAsync(() -> {int j = 0;System.out.println("j = " + j);});Void unused = future01.get();System.out.println("unused = " + unused);}
}

2.3.2可以接收值但是没有返回值

thenAccept方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。

    public CompletableFuture<Void> thenAccept(Consumer<? super T> action) {return uniAcceptStage(null, action);}public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) {return uniAcceptStage(asyncPool, action);}public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor) {return uniAcceptStage(screenExecutor(executor), action);}

2.3.3可以接收值也可以返回值

thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。

    public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) {return uniApplyStage(null, fn);}public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn) {return uniApplyStage(asyncPool, fn);}public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor) {return uniApplyStage(screenExecutor(executor), fn);}

代码示例

package com.atguigu.gmall.product.completableFuture;import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class Test03 {public static ExecutorService excutor =Executors.newFixedThreadPool(10);public static void main(String[] args) throws ExecutionException, InterruptedException {CompletableFuture<Integer> future03 = CompletableFuture.supplyAsync(() -> {int i = 0;System.out.println("i = " + i);return i;}).thenApplyAsync((res) -> {res++;return res;});Integer integer = future03.get();System.out.println("integer = " + integer);}
}

带有Async默认是异步执行的。这里所谓的异步指的是不在当前线程内执行。

Function<? super T,? extends U>
T:上一个任务返回结果的类型
U:当前任务的返回值类型

2.4两任务组合-一个完成即可

2.5两任务组合-两个都要完成

2.6多任务组合

2.7查看商品详情实战

 @Autowiredprivate ThreadPoolExecutor executor;public Map<String, Object> getBySkuId(Long skuId) {Map<String, Object> result = new HashMap<>();//添加布隆过滤器 每次添加skuinfo信息的时候,都会把skuid放在布隆过滤器中,这样查询skuinfo时,// 首先进行检查是否通过布隆过滤器,通过说明在数据库中存在该数据。不通过说明数据库不存在该数据。// 布隆过滤器可以解决缓存穿透的问题。RBloomFilter<Object> bloomFilter = redissonClient.getBloomFilter(RedisConst.SKU_BLOOM_FILTER);if (!bloomFilter.contains(skuId)) return result;//添加异步任务 查询skuInfoCompletableFuture<SkuInfo> skuInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);if (skuInfo == null){return skuInfo;}result.put("skuInfo",skuInfo);return skuInfo;}, executor);//  获取分类数据CompletableFuture<Void> categoryViewCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync((skuInfo) -> {BaseCategoryView categoryView = productFeignClient.getCategoryView(skuInfo.getCategory3Id());result.put("categoryView", categoryView);});//  获取销售属性+销售属性值CompletableFuture<Void> spuSaleAttrListCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync((skuInfo -> {List<SpuSaleAttr> spuSaleAttrListCheckBySku = productFeignClient.getSpuSaleAttrListCheckBySku(skuId, skuInfo.getSpuId());result.put("spuSaleAttrList", spuSaleAttrListCheckBySku);}));//  查询销售属性值Id 与skuId 组合的mapCompletableFuture<Void> valuesSkuJsonCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync(skuInfo -> {Map skuValueIdsMap = productFeignClient.getSkuValueIdsMap(skuInfo.getSpuId());//  将这个map 转换为页面需要的Json 对象String valueJson = JSON.toJSONString(skuValueIdsMap);result.put("valuesSkuJson", valueJson);});//  spu海报数据CompletableFuture<Void> spuPosterListCompletableFuture = skuInfoCompletableFuture.thenAcceptAsync(skuInfo -> {//  返回map 集合 Thymeleaf 渲染:能用map 存储数据!List<SpuPoster> spuPosterList = productFeignClient.getSpuPosterBySpuId(skuInfo.getSpuId());result.put("spuPosterList", spuPosterList);});//  获取价格CompletableFuture<Void> skuPriceCompletableFuture = CompletableFuture.runAsync(() -> {BigDecimal skuPrice = productFeignClient.getSkuPrice(skuId);//  map 中 key 对应的谁? Thymeleaf 获取数据的时候 ${skuInfo.skuName}result.put("price", skuPrice);});CompletableFuture<Void> skuAttrListCompletableFuture = CompletableFuture.runAsync(() -> {List<BaseAttrInfo> attrList = productFeignClient.getAttrList(skuId);//  使用拉姆达表示List<Map<String, String>> skuAttrList = attrList.stream().map((baseAttrInfo) -> {Map<String, String> attrMap = new HashMap<>();attrMap.put("attrName", baseAttrInfo.getAttrName());attrMap.put("attrValue", baseAttrInfo.getAttrValueList().get(0).getValueName());return attrMap;}).collect(Collectors.toList());result.put("skuAttrList", skuAttrList);});//阻塞主线程等待总的结果CompletableFuture<Void> future = CompletableFuture.allOf(skuInfoCompletableFuture, categoryViewCompletableFuture,spuSaleAttrListCompletableFuture, valuesSkuJsonCompletableFuture,spuPosterListCompletableFuture, skuPriceCompletableFuture,skuAttrListCompletableFuture);future.join();return result;}

修改之前
在这里插入图片描述
修改之后
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/345435.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

视觉SLAM十四讲:从理论到实践(Chapter9:后端1)

前言 学习笔记&#xff0c;仅供学习&#xff0c;不做商用&#xff0c;如有侵权&#xff0c;联系我删除即可 一、目标 1.理解后端的概念。 2.理解以EKF为代表的滤波器后端的工作原理。 3.理解非线性优化的后端&#xff0c;明白稀疏性是如何利用的。 4.使用g2o和Ceres实际操作…

报表或者BI的价值在哪?这是十几年的问题啦!

对&#xff0c;问题已经十几年了&#xff0c;答案也应该普世都懂了吧&#xff0c;但非常遗憾&#xff0c;答案没有问题普及的广。看似简单&#xff0c;但也难说清楚&#xff0c;不同的人&#xff0c;总会有不同的看法。 为什么要解释这个并不新鲜的问题&#xff1f; 因为有人问…

阅读笔记:Multi-threaded Rasterization in the Chromium Compositor

Multi-threaded Rasterization in the Chromium Compositor PPT 原始链接&#xff1a; https://docs.google.com/presentation/d/1nPEC4YRz-V1m_TsGB0pK3mZMRMVvHD1JXsHGr8I3Hvc/edit?uspsharing PPT主要介绍了Chromium浏览器中使用多线程光栅化(Impl-side painting)的机制&a…

基于NodeJs 的Vue安装和创建项目

基于NodeJs 的Vue安装和创建项目 一、Node.js的下载与安装 下载地址&#xff1a; https://nodejs.org/en/download/prebuilt-installer 安装完之后&#xff0c;启动 cmd命令行&#xff0c;验证 Node.js 是否安装成功 二、配置npm的全局模块的存放路径以及缓存的路径 注&…

【简单介绍下DALL-E2,什么是DALL-E2?】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

【数据结构】 -- 堆 (堆排序)(TOP-K问题)

引入 要学习堆&#xff0c;首先要先简单的了解一下二叉树&#xff0c;二叉树是一种常见的树形数据结构&#xff0c;每个节点最多有两个子节点&#xff0c;通常称为左子节点和右子节点。它具有以下特点&#xff1a; 根节点&#xff08;Root&#xff09;&#xff1a;树的顶部节…

idea如何根据路径快速在项目中快速打卡该页面

在idea项目中使用快捷键shift根据路径快速找到该文件并打卡 双击shift(连续按两下shift) -粘贴文件路径-鼠标左键点击选中跳转的路径 自动进入该路径页面 例如&#xff1a;我的实例路径为src/views/user/govType.vue 输入src/views/user/govType或加vue后缀src/views/user/go…

Cweek4+5

C语言学习 十.指针详解 6.有关函数指针的代码 代码1&#xff1a;(*(void (*)())0)(); void(*)()是函数指针类型&#xff0c;0是一个函数的地址 (void(*)())是强制转换 总的是调用0地址处的函数&#xff0c;传入参数为空 代码2&#xff1a;void (*signal(int, void(*)(int))…

以客户为中心:消费电子行业的产品研发之道

在消费电子行业这片快速变化的领域中&#xff0c;产品的迭代更新和技术的创新是推动行业不断前进的动力。然而&#xff0c;随着市场的日益成熟和消费者需求的多样化&#xff0c;如何确保产品能够满足目标用户的需求&#xff0c;成为摆在每一个产品研发团队面前的难题。本文将探…

JVM垃圾收集器和性能调优

目标&#xff1a; 1.JVM垃圾收集器有哪几种&#xff1f; 2.CMS垃圾收集器回收步骤。 一、JVM常见的垃圾回收器 为什么垃圾回收的时候需要STW? 标记垃圾的时候&#xff0c;如果不STW&#xff0c;可能用户线程就会不停的产生垃圾。 1.1 单线程收集 Serial和SerialOld使用单…

如何下载BarTender软件及详细安装步骤

BarTender是美国海鸥科技推出的一款优秀的条码打印软件&#xff0c;应用于 WINDOWS95 、 98 、 NT 、 XP 、 2000 、 2003 和 3.1 版本&#xff0c; 产品支持广泛的条形码码制和条形码打印机&#xff0c; 不但支持条形码打印机而且支持激光打印机&#xff0c;还为世界知名品牌条…

基于R语言BIOMOD2 及机器学习方法的物种分布模拟与案例分析

原文链接&#xff1a;基于R语言BIOMOD2 及机器学习方法的物种分布模拟与案例分析https://mp.weixin.qq.com/s?__bizMzUzNTczMDMxMg&mid2247606139&idx4&snf94ec30bfb5fa7ac0320403d49db3b66&chksmfa821e9ccdf5978a44a9ba96f6e04a121c0bbf63beea0940b385011c0b…

联合体和枚举<C语言>

导言 在C语言中除了结构体外&#xff0c;联合体和枚举也是自定义类型&#xff0c;联合体主要用于节省空间&#xff0c;在同一块内存存储多种类型的数据&#xff0c;而枚举可以提高代码的可读性、可维护性。 联合体&#xff08;union&#xff09; 它还有个更容易理解的名字&…

力扣2444.统计定界子数组的数目

力扣2444.统计定界子数组的数目 观察到不满足条件的数 可以作为天然的分割线 因此在枚举右端点的过程中 预处理minK&#xff0c;maxK和分割线上一次出现的下标 res min(min_i,max_i) - i0; 但是因为可能在到下个区段时 min_i和max_i尚未更新 导致结果为负数 所以要跟0再取一…

【devops】 Bytebase 一站式开源 数据库DevOps平台

初识 Bytebase 1、安装 安装地址 https://www.bytebase.com/docs/get-started/self-host/#docker 安装指令 docker run --init \--name bytebase \--publish 8080:8080 --pull always \--volume ~/.bytebase/data:/var/opt/bytebase \bytebase/bytebase:2.18.02、登录-dashboa…

红黑树的介绍与实现

前言 前面我们介绍了AVL树&#xff0c;AVL树是一棵非常自律的树&#xff0c;有着严格的高度可控制&#xff01;但是正它的自律给他带来了另一个问题&#xff0c;即虽然他的查找效率很高&#xff0c;但是插入和删除由于旋转而导致效率没有那么高。我们上一期的结尾说过经常修改…

为何PHP使用率 大幅度下降!需求量几乎为零!

用PHP的人越来越少的主要原因包括&#xff1a;市场竞争加剧、新技术的出现、性能和安全问题、以及开发者社区的变化。市场竞争加剧是其中一个突出的因素。随着Python、Node.js等现代编程语言的崛起&#xff0c;它们提供了更好的性能、更简洁的语法和更丰富的框架&#xff0c;逐…

技术与业务的完美融合:大数据BI如何真正提升业务价值

数据分析有一点经典案例 沃尔玛的啤酒和尿布案例 开始做BI的时候&#xff0c;大家肯定都看过书&#xff0c;那么一定也看过一个经典的案例&#xff0c;就是沃尔玛的啤酒和尿布的案例。这个案例确实很经典&#xff0c;但其实是一个失败的案例。为什么这么说呢&#xff1f;很明显…

Spring Boot 实现动态数据源配置

前言 之前在CSDN博客以及某站看了教程&#xff0c;不免觉得有点不知如何下手&#xff0c;好在最后融合了以下&#xff0c;得出了一个比较简单的配置动态数据源的过程。 首先项目是Spring Boot的单体项目&#xff0c;我们的需求是要连接多个数据库&#xff0c;那么就需要配置多个…

Nvidia/算能 +FPGA+AI大算力边缘计算盒子:大疆RoboMaster AI挑战赛

NVIDIA Jetson TX2助力机器人战队斩获RoboMaster AI挑战赛冠亚军 一个汇聚数百万机器人专家与研究人员的赛场&#xff0c;一场兼具工程、策略和团队挑战的较量&#xff0c;说的正是近日刚刚在澳大利亚布里斯本ICRA大会上闭幕的大疆RoboMaster AI挑战赛今年的冠军I Hiter以及亚军…