Blogs

CompletableFuture异步编排

简介

  • CompletableFuture是java.utils.concurrent里的一个类

  • 作用:把串行执行的代码变为并行执行,提高代码执行速度

快速上手

创建异步编排对象

public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier);
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor);

public static CompletableFuture<Void> runAsync(Runnable runnable);
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor);

线程串行方法

// 使线程串行执行,无入参,无返回值
public CompletableFuture<Void> thenRun(Runnable action);
public CompletableFuture<Void> thenRunAsync(Runnable action);
public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor);

// 使线程串行执行,有入参,无返回值
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);

// 使线程串行执行,有入参,有返回值
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);

多任务组合

public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);

代码实例

public class CompletableFutureTest5 {

    @SneakyThrows
    public static void main(String[] args) {
        //动态获取服务器核数
        int processors = Runtime.getRuntime().availableProcessors();
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                processors+1, // 核心线程个数 io:2n ,cpu: n+1  n:内核数据
                processors+1,
                0,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(10),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy()
        );

        CompletableFuture<String> future01 = CompletableFuture.supplyAsync(() -> "任务1", executor);
        CompletableFuture<String> future02 = CompletableFuture.supplyAsync(() -> "任务2", executor);
        CompletableFuture<String> future03 = CompletableFuture.supplyAsync(() -> {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return "任务3";
        }, executor);

        // 串联起若干个线程任务, 没有返回值
        CompletableFuture<Void> all = CompletableFuture.allOf(future01, future02, future03);
        // 等待所有线程执行完成
        // .join()和.get()都会阻塞并获取线程的执行情况
        // .join()会抛出未经检查的异常,不会强制开发者处理异常 .get()会抛出检查异常,需要开发者处理
        all.join();
        all.get();
    }
}  

MongoDB

概述

  • MongoDB 与传统关系型数据库相比更加简单,架构为key-value结构
  • MySQL数据库:数据库-表-记录 MongoDB :数据库-集合-文档(记录)
  • 文档类似于JSON对象,结构成为BSON

安装与启动

1. 导入 MongoDB 官方 GPG 密钥

首先需要导入 MongoDB 官方的 GPG 密钥,以便系统能够验证下载包的完整性。

curl -fsSL https://pgp.mongodb.com/server-7.0.asc | \
   sudo gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg \
   --dearmor

2. 添加 MongoDB 软件源

将 MongoDB 的软件源添加到系统的源列表中,这样就可以通过包管理器直接下载 MongoDB。

分布式事物锁

分布式锁解决司机抢单

因为上面写的司机抢单并没有考虑并发,类似于电商的超卖问题

**解决方案

  • 第一种 设置数据库事务的隔离级别,设置为Serializable,效率低下

  • 第二种 使用乐观锁解决,通过版本号进行控制

  • 第三种 加锁解决,学习过synchronized 及lock锁,本地锁,目前微服务架构,分布式部署方式。

本地锁的局限性

  • 我们使用锁一般都是,synchronized 及lock锁,这些都是本地锁,只在当前jvm生效,在微服务里面就是只有当个微服务生效
  • 举例演示 TestController
@Tag(name = "测试接口")  
@RestController  
@RequestMapping("/order/test")  
public class TestController {  
  
    @Autowired  
    private TestService testService;  
  
    @GetMapping("testLock")  
    public Result testLock() {  
        testService.testLock();  
        return Result.ok();  
    }  
}

TestServiceImpl

快速上手xxl-job

XXL-JOB入门案例

第一步

下载XXL-JOB示例代码,解压,使用idea打开

**项目组成:

  • admin:调度中心
  • core:公共依赖
  • sample
    • sample-frameless:不带框架的
    • sample-springboot:带springboot框架的

第二步

创建XXL-JOB使用数据库和相关表

什么是循环依赖

什么是循环依赖

现在我们有两个类 ClassA 与 ClassB,但他们互相引用,直接或间接依赖对方,例如例如A类里有B的对象,B类中又有A的对象

public class ClassA {
    private ClassB classB;
    // 构造方法、getter 和 setter 等
}

public class ClassB {
    private ClassA classA;
    // 构造方法、getter 和 setter 等
}

这种依赖不仅限于出现在类上,也可能会出现在包上或者模块上

  • **出现在包层面的危害:

    • 增加了代码的耦合度,降低了代码的可维护性和可读性
    • 在编译时可能会出现编译顺序的问题,难以确定先编译哪个包。
    • 在后续的开发和维护过程中,增加了维护的难度和风险。
  • **出现在模块层面的危害:

    • 会导致模块之间的边界变得模糊,无法清晰地划分模块的职责。在构建和部署项目时,可能会出现循环加载的问题,影响项目的启动效率和运行性能。

Spring中的循环依赖

当两个或多个 Bean 之间存在构造器注入的循环依赖时,Spring 无法解决这种依赖关系。例如,BeanA 的构造器需要 BeanB,而 BeanB 的构造器又需要 BeanA。Spring 容器在创建这些 Bean 的过程中会陷入死循环,无法确定先创建哪个 Bean,从而抛出 BeanCurrentlyInCreationExceptionBeanCurrentlyInCreationException 等异常。