CompletableFuture 异步编排

news2024/11/20 15:30:16

目录

  • CompletableFuture 的详解
  • 代码测试
  • 配置类的引入
  • Demo1
  • Demo2
  • CompletableFuture的async后缀函数与不带async的函数的区别
  • ThreadPoolTaskExecutor 和 ThreadPoolExecutor 的区别
  • Spring 线程池的使用
  • 业务使用多线程的原因
  • 场景一:
  • 场景二:
  • FutureTask介绍
  • 线程池为什么要使用阻塞队列
  • Spring 常用的线程池的使用
  • 序列
  • 常规使用
  • 异步使用

CompletableFuture 的详解

 

🍫 它就是创建一个异步任务,然后在干什么,可以使用多任务组合

 
  • 创建任务的方法
static CompletableFuture<Void> runAsync(Runnable runnable)
public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier)
public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor)
  • 然后继续上一段的任务(里面包含了串行,AND,OR)

🚩 串行:

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 CompletionStage<Void> thenAccept(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action);
public CompletionStage<Void> thenAcceptAsync(Consumer<? super T> action,Executor executor);

public CompletionStage<Void> thenRun(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action);
public CompletionStage<Void> thenRunAsync(Runnable action,Executor executor);
Function<? super T,? extends U>
T:上一个任务返回结果的类型
U:当前任务的返回值类型
 

参数解析:

  • thenApply 方法:当一个线程依赖另一个线程时,获取上一个任务返回的结果,并返回当前任务的返回值。(接收上一阶段任务结果,返回结果)
  • thenAccept方法:消费处理结果。接收任务的处理结果,并消费处理,无返回结果。 (接收上一阶段任务结果,不返回结果)
  • thenRun方法:不接收上一阶段任务结果,并且无返回值 带有Async默认是异步执行的。这里所谓的异步指的是不在当前线程内执行。

🚡 AND

public <U,V> CompletionStage<V> thenCombineAsync (CompletionStage<? extends U> other,BiFunction<? super T,? super U,? extends V> fn,Executor executor);
- 上一阶段任务与other任务均执行结束,接收两个任务的结果,并可获取返回值

public <U> CompletionStage<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn,
         Executor executor);
- 使用上一阶段任务的结果,返回一个新的CompletableFuture实例
 

更多的参数详解: 博客链接

 
  • 多任务组合
 
public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs);

public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs);

代码测试

 
配置类的引入

yml

thread:
  pool:
    corePoolSize: 4
    maxPoolSize: 8
    workQueue: 25
    keepAliveTime: 30
 

config

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
@ConfigurationProperties(prefix = "thread.pool")
public class AsyncConfig{
    //核心线程数量大小
    private   int corePoolSize = 4;
    //线程池最大容纳线程数
    private   int maxPoolSize =8;
    //阻塞队列
    private   int workQueue = 25;
    //线程空闲后的存活时长
    private   int keepAliveTime = 30;

    @Bean("asyncTaskExecutor")
    public Executor getAsyncExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        //核心线程数
        threadPoolTaskExecutor.setCorePoolSize(corePoolSize);
        //最大线程数
        threadPoolTaskExecutor.setMaxPoolSize(maxPoolSize);
        //等待队列
        threadPoolTaskExecutor.setQueueCapacity(workQueue);
        //线程前缀
        threadPoolTaskExecutor.setThreadNamePrefix("taskExecutor-");
        //线程池维护线程所允许的空闲时间,单位为秒
        threadPoolTaskExecutor.setKeepAliveSeconds(keepAliveTime);
        // 线程池对拒绝任务(无线程可用)的处理策略
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }
}

🍫 参数详解: 建议看看下面的线程池对拒绝任务(无线程可用)的处理策略

  1、corePoolSize:核心线程数
        * 核心线程会一直存活,及时没有任务需要执行
        * 当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理
        * 设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭
 
    2、queueCapacity:任务队列容量(阻塞队列)
        * 当核心线程数达到最大时,新任务会放在队列中排队等待执行
 
    3、maxPoolSize:最大线程数
        * 当线程数>=corePoolSize,且任务队列已满时。线程池会创建新线程来处理任务
        * 当线程数=maxPoolSize,且任务队列已满时,线程池会拒绝处理任务而抛出异常
 
    4、 keepAliveTime:线程空闲时间
        * 当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
        * 如果allowCoreThreadTimeout=true,则会直到线程数量=0
 
    5、allowCoreThreadTimeout:允许核心线程超时
    6、rejectedExecutionHandler:任务拒绝处理器
        * 两种情况会拒绝处理任务:
            - 当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务
            - 当线程池被调用shutdown()后,会等待线程池里的任务执行完毕,再shutdown。如果在调用shutdown()和线程池真正shutdown之间提交任务,会拒绝新任务
        * 线程池会调用rejectedExecutionHandler来处理这个任务。如果没有设置默认是AbortPolicy,会抛出异常
        * ThreadPoolExecutor类有几个内部实现类来处理这类情况:
            - AbortPolicy 丢弃任务,抛运行时异常(默认)
            - CallerRunsPolicy 执行任务
            - DiscardPolicy 忽视,什么都不会发生
            - DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
        * 实现RejectedExecutionHandler接口,可自定义处理器


 

Demo1
  

image-20221115170031342

 
        CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("洗水壶");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            return "水壶";
        }).thenApply(e->{
            System.out.println("烧水");
            try {
                Thread.sleep(5000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            return "热水";
        });
        //洗水壶->洗水杯->拿茶叶
        CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("洗茶壶");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            return "茶壶";
        }).thenApply(e->{
            try {
                Thread.sleep(2000);
            } catch (InterruptedException ex) {
                ex.printStackTrace();
            }
            System.out.println("洗水杯");
            return "水杯";
        }).thenApply(e->{
            System.out.println("拿茶叶");
            return "茶叶";
        });
        //泡茶
        CompletableFuture<String> task3 = task1.thenCombine(task2, (a, b) -> {
            System.out.println("泡茶");
            return "茶";
        });
        String tea = task3.join();
        System.out.println(tea);
 

🍫 参数解析:

更多的参数详解: 博客链接

Demo2
 
  • 这个测试我就没有写了,自己可以看看

问题:当查询接口较复杂时候,数据的获取都需要远程调用,必然需要花费更多的时间。

假如查询文章详情页面,需要如下标注的时间才能完成:

 


// 1. 查询文章详情 0.5s

// 2. 查询文章博主个人信息 0.5s

// 3. 查询文章评论 1s

// 4. 查询博主相关文章分类 1s

// 5. 相关推荐文章 1s

// ......
上面的描述只是举个例子不要在意这里的查询描述,看实际情况使用,有些相关的查询我们可以拆分接口实现,上面的描述只是为了举例子。
 

@Service
public class ArticleService {
	@Autowired
    private ArticleClient articleClient;
    @Autowired
    private UserClient userClient;
    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    
 	public ItemVo load(Long id) {
	// 1. 查询文章详情 0.5s
	// 下面的查询需要用到文章对应的发布用户,所以这里需要使用CompletableFuture.supplyAsync
	CompletableFuture<ArticleEntity> articleCompletableFuture = CompletableFuture.supplyAsync(() -> {
            ResponseVo<ArticleEntity> skuEntityResponseVo = this.articleClient.getArticleById(id);
            ArticleEntity articleEntity = skuEntityResponseVo.getData();
            if (articleEntity == null) {
                return null;
            }
            itemVo.setId(id);
            itemVo.setTitle(articleEntity.getTitle());
            itemVo.setDefaltImage(articleEntity.getDefaultImage());
            return articleEntity;
        }, threadPoolExecutor);
	// 2. 查询文章博主个人信息 0.5s
	// 这里查询需要依赖文章关联的用户id,所以需要使用articleCompletableFuture.thenAcceptAsync()
    CompletableFuture<Void> userCompletableFuture = articleCompletableFuture.thenAcceptAsync(articleEntity -> {
        ResponseVo<UserEntity> categoryResponseVo = this.userClient.queryUserInfoById(articleEntity.getUserId());
        UserEntity userEntity = categoryResponseVo.getData();
        itemVo.setUserInfo(userEntity);
    }, threadPoolExecutor);    
	// 3. 查询博主相关文章分类 1s
	// 这里查询需要依赖文章关联的用户id,所以需要使用articleCompletableFuture.thenAcceptAsync()
    CompletableFuture<Void> userOtherArticleCompletableFuture = articleCompletableFuture.thenAcceptAsync(articleEntity -> {
        ResponseVo<List<UserAuserOtherArticleEntity>> categoryResponseVo = this.articleClient.queryUserAuserOtherArticleById(articleEntity.getUserId());
        UserAuserOtherArticleEntity userAuserOtherArticleEntity = categoryResponseVo.getData();
        itemVo.setUserAuserOtherArticleList(userAuserOtherArticleEntity);
    }, threadPoolExecutor);
    // 4. 查询文章评论 1s
    // 不需要依赖其他请求返回值,可以使用新的异步对象 CompletableFuture.runAsync()
    CompletableFuture<Void> commentsCompletableFuture =  CompletableFuture.runAsync(() -> {
        ResponseVo<List<UserArticleCategoryEntity>> userArticleCategoryVo = this.userClient.queryCommentsByArticleId(id);
        UserArticleCategoryEntity userArticleCategoryEntity = userArticleCategoryVo.getData();
        itemVo.setUserArticleCategoryList(userArticleCategoryEntity);
    }, threadPoolExecutor);
	// 5. 相关推荐文章 1s
	// 不需要依赖其他请求返回值,可以使用新的异步对象 CompletableFuture.runAsync()
	CompletableFuture<Void> relatedArticlesCompletableFuture =  CompletableFuture.runAsync(() -> {
        ResponseVo<List<RelatedArticlesEntity>> userArticleCategoryVo = this.articleClient.queryRelatedArticles(id);
        UserArticleCategoryEntity userArticleCategoryEntity = userArticleCategoryVo.getData();
        itemVo.setUserArticleCategoryList(userArticleCategoryEntity);
    }, threadPoolExecutor);
	}
	// 多任务执行组合 CompletableFuture.allOf()
	CompletableFuture.allOf(articleCompletableFuture, userCompletableFuture, userOtherArticleCompletableFuture,
                commentsCompletableFuture, relatedArticlesCompletableFuture).join();
     return itemVo;
}


 

CompletableFuture的async后缀函数与不带async的函数的区别

 

参考链接: 博客链接

 

🍫 结论:

不带async的函数的动作比较复杂

f的whenComplete的内容由哪个线程来执行,取决于哪个线程X执行了f.complete()。但是当X线程执行了f.complete()的时候,whenComplete还没有被执行到的时候(就是事件还没有注册的时候),那么X线程就不会去同步执行whenComplete的回调了。这个时候哪个线程执行到了whenComplete的事件注册的时候,就由哪个线程自己来同步执行whenComplete的事件内容。

而whenCompleteAsync的场合,就简单很多。一句话就是线程池里面拿一个空的线程或者新启一个线程来执行回调。和执行f.complete的线程以及执行whenCompleteAsync的线程无关。



 

ThreadPoolTaskExecutor 和 ThreadPoolExecutor 的区别

 

参考链接: 博客链接

🍫 结论:

其实最主要的原因很直观:ThreadPoolExecutor是一个不受Spring管理生命周期、参数装配的Java类,而有了ThreadPoolTaskExecutor的封装,线程池才有Spring“内味”。

 

Spring 线程池的使用


 

业务使用多线程的原因

 
  • 目的是面对高并发的时候,提高运行速度

场景一:

一个业务逻辑有很多次的循环,每次循环之间没有影响,比如验证1万条url路径是否存在,正常情况要循环1万次,逐个去验证每一条URL,这样效率会很低,假设验证一条需要1分钟,总共就需要1万分钟,有点恐怖。这时可以用多线程,将1万条URL分成50等份,开50个线程,没个线程只需验证200条,这样所有的线程执行完是远小于1万分钟的。


 

场景二:

需要知道一个任务的执行进度,比如我们常看到的进度条,实现方式可以是在任务中加入一个整型属性变量(这样不同方法可以共享),任务执行一定程度就给变量值加1,另外开一个线程按时间间隔不断去访问这个变量,并反馈给用户。总之使用多线程就是为了充分利用cpu的资源,提高程序执行效率,当你发现一个业务逻辑执行效率特别低,耗时特别长,就可以考虑使用多线程。

问题:不过CPU执行哪个线程的时间和顺序是不确定的,即使设置了线程的优先级,因此使用多线程的风险也是比较大的,会出现很多预料不到的问题,一定要多熟悉概念,多构造不同的场景去测试才能够掌握!

项目中可以通过:

@Order()
设置运行的优先级,数字越小,级别越高


 

FutureTask介绍

 

🍫 参考: 博客链接

线程池为什么要使用阻塞队列

 

阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait 状态,释放 cpu 资源,当队列中有任务时才唤醒对应线程从队列中取出消息进行执行。

使得在线程不至于一直占用cpu资源。(线程执行完任务后通过循环再次从任务队列中取出任务进行执行,代码片段如:while (task != null || (task = getTask()) != null) {})。

不用阻塞队列也是可以的,不过实现起来比较麻烦而已,有好用的为啥不用呢
 

Spring 常用的线程池的使用

 

序列

Spring 通过任务执行器(TaskExecutor)来实现多线程和并发编程,使用 ThreadPoolTaskExecutor 实现一个基于线程池的TaskExecutor,
还得需要使用 @EnableAsync 开启异步,并通过在需要的异步方法那里使用注解@Async声明是一个异步任务

Spring 已经实现的异常线程池:

  •  
  • - SimpleAsyncTaskExecutor:不是真的线程池,这个类不重用线程,每次调用都会创建一个新的线程。
  • - SyncTaskExecutor:这个类没有实现异步调用,只是一个同步操作。只适用于不需要多线程的地方
  • - ConcurrentTaskExecutor:Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类
  • - SimpleThreadPoolTaskExecutor:是Quartz的 SimpleThreadPool 的类。线程池同时被quartz和非quartz使用,才需要使用此类
  • - ThreadPoolTaskExecutor :最常使用,推荐。 其实质是对 java.util.concurrent.ThreadPoolExecutor 的包装
     

🍫 扩展:相信大家在 Java 里面也学过 JUC ,里面有 Java 里面的线程池,可以直接去看看 ThreadPoolExecutor

至于为什么有线程池,Spring 为什么还有在自己搞一个,可以自己去探索,Spring 的底层还是 ThreadPoolExecutor ,只是它的生命周期不受它控制。


 

常规使用

 
//    线程池(config里面的Bean)
    @Autowired
    private Executor taskExecutor;
    
Callable<ScyTeacher> scy =()-> scyTeacherMapper.selectOne(new LambdaQueryWrapper<ScyTeacher>()
                                      .eq(ScyTeacher::getUsername,test));
FutureTask<ScyTeacher> commentCallable = new FutureTask<>(scy);
Future<Map> submit = executor.submit(commentCallable);
Map map = submit.get();

异步使用

 

🚩 记得开启异步(配置类添加)

@EnableAsync //开启异步执行
 
package com.melodyjerry.thread;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

/**
 * @classname AsyncTaskService
 * @description 异步任务的执行类
 */
@Service
public class AsyncTaskService {
    @Async //异步方法
    public void executeAsyncTask(Integer i) {
        System.out.javaprintln("执行异步任务: "+i);
    }

    @Async //异步方法
    public void executeAsyncTaskPlus(Integer i) {
        System.out.println("执行异步任务+1: " + (i+1));
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(三) Markdown插入互联网或本地视频解决方案

前言 不论博客系统是WordPress还是Typecho&#xff0c;绕不开的是两种书写语言&#xff0c;一种称之为富文本&#xff0c;一种叫做Markdown。 Markdown有很多好处&#xff0c;也有很多坏处&#xff0c;比如Markdown本身不具备段落居中的功能&#xff0c;以及Markdown也不具有…

聊聊JAVA中的锁优化锁升级及其底层原理剖析

文章目录 1. 基础介绍java中的锁什么是锁优化和锁升级 2. Java中的锁升级过程及底层原理1. 偏向锁实现机制和原理1. 偏向锁的原理2. 偏向锁的底层实现 2. 轻量级锁1. 轻量级锁的原理2. 轻量级锁的底层实现 3. 重量级锁1. 重量级锁的原理2. 重量级锁的底层实现 3. Java中锁升级的…

selenium +IntelliJ+firefox/chrome 环境全套搭配

1第一步&#xff1a;下载IntelliJ idea 代码编辑器 2第二步&#xff1a;下载浏览器Chrome 3第三步&#xff1a;下载JDK 4第四步&#xff1a;配置环境变量&#xff08;1JAVA_HOME 2 path&#xff09; 5第五步&#xff1a;下载Maven 6第六步&#xff1a;配置环境变量&#x…

新环境下配置jupyter notebook并启动

1.先切换到我们需要使用的环境 2.安装python内核 conda install ipykernel 3.创建指定内核 ipython kernel install --user --nametensorflow2 4.运行jupyter notebook 即出现新创建的内核

计算机网络 第一章计算机网络体系结构

非常好的课程。计算机网络 //----------------------------------------------------------------------------------------------------------------// 需要清楚实体、协议、服务。 网络协议三要素&#xff0c;语法、语义、同步。

遗留系统陷入困境

当我们谈论遗留系统时&#xff0c;我们经常会想到数据中心某处的过时服务器和交换机。我们带着一种病态的迷恋阅读了有关系统性技术问题的文章&#xff0c;这些问题在假期周末困扰着其他旅行者&#xff0c;并为他们缺乏远见而摇头。 然后&#xff0c;我们坐在屏幕前&#xff0…

数据结构—栈、队列、链表

一、栈 Stack&#xff08;存取O(1)&#xff09; 先进后出&#xff0c;进去123&#xff0c;出来321。 基于数组&#xff1a;最后一位为栈尾&#xff0c;用于取操作。 基于链表&#xff1a;第一位为栈尾&#xff0c;用于取操作。 1.1、数组栈 /*** 基于数组实现的顺序栈&#…

4 Ways to Fix an Operation Did Not Complete Virus Error on Windows

文章目录 Can’t open a file on your PC? Use these methods to fix your issue. A Windows operation did not complete virus error.Mahesh Makvana / How-To Geek Readers like you help support How-To Geek. When you make a purchase using links on our site, we ma…

Spring实例化源码解析之registerBeanPostProcessors(六)

BeanPostProcessors是Spring框架中的一个扩展机制&#xff0c;它允许开发人员在Spring容器实例化、配置和初始化Bean的过程中干预和定制化。BeanPostProcessor接口定义了两个方法&#xff1a;postProcessBeforeInitialization和postProcessAfterInitialization&#xff0c;分别…

【WSN】无线传感器网络 X-Y 坐标到图形视图和位字符串前缀嵌入方法研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

MacOS - Sonoma更新了啥

1 系统介绍 苹果公司于2023年9月26日发布了macOS Sonoma 14.0正式版。名称由来不知道&#xff0c;可能是地名&#xff1a;Sonoma是一个地名,指加利福尼亚州北部索诺玛县(Sonoma County)。 2 系统重要更新 2.1 将小组件添加到桌面 速览提醒事项和临近日程等。按住Control键点…

(一) 使用 Hugo 搭建个人博客保姆级教程(上篇)

手把手教你如何从0开始构建一个静态网站&#xff0c;这不需要有太多的编程和开发经验和时间投入&#xff0c;也基本不需要多少成本&#xff08;除了个性化域名&#xff09;&#xff0c;使用GitHub和Hugo模板即可快速构建和上线一个网站。 目标读者 本文档适用于以下用户&…

学信息系统项目管理师第4版系列16_资源管理过程

1. 组建项目团队&#xff0c;建设项目团队和管理项目团队属于执行过程组 1.1. 【高22上选21】 1.1.1. 【高21上选25】 1.2. 3版 2. 【高19上案三】 2.1. 【高18上案三】 2.2. 【高23上案一】 3. 规划资源管理 3.1. 定义如何估算、获取、管理和利用团队以及实物资源的过…

二叉树的顺序存储——堆——初识堆排序

前面我们学过可以把完全二叉树存入到顺序表中&#xff0c;然后利用完全二叉树的情缘关系&#xff0c;就可以通过数组下标来联系。 但是并不是把二叉树存入到数组中就是堆了&#xff0c;要看原原来的二叉树是否满足&#xff1a;所有的父都小于等于子&#xff0c;或者所有的父都…

十二、【漏洞复现】Rails任意文件读取(CVE-2019-5418)

十二、【漏洞复现】Rails任意文件读取(CVE-2019-5418&#xff09; 12.1、漏洞原理 Ruby on Rails是一个使用 Ruby 语言写的开源 Web 应用框架&#xff0c;它是严格按照 MVC 结构开发的。它努力使自身保持简单&#xff0c;来使实际的应用开发时的代码更少&#xff0c;使用最少…

JavaScript系列从入门到精通系列第十三篇:JavaScript中基本数据类型和引用数据类型,创建对象的两种方式

一&#xff1a;基本数据类型与引用数据类型 基本数据类型&#xff1a;String Number Boolean Null Undefined 引用数据类型&#xff1a;Object 我们的内存分成了两大块&#xff0c;一是栈内存二是堆内存。变量都是保存到栈内存中&#xff0c;var a 123; a和123都在栈空间&…

互联网Java工程师面试题·ZooKeeper 篇·第一弹

目录 1. ZooKeeper 面试题&#xff1f; 2. ZooKeeper 提供了什么&#xff1f; 3. Zookeeper 文件系统 4. ZAB 协议&#xff1f; 5. 四种类型的数据节点 Znode 6. Zookeeper Watcher 机制 -- 数据变更通知 7. 客户端注册 Watcher 实现 8. 服务端处理 Watcher 实现 9. 客…

Redis最常见的5种应用场景

Redis作为当今最流行的内存数据库&#xff0c;已经成为服务端加速的必备工具之一。对于Redis为什么那么快&#xff1f;以及Redis采用单线程&#xff0c;但为什么反而获得更高的性能的疑问&#xff0c;在之前的Redis为什么那么快&#xff1f;一文中&#xff0c;已经有所介绍。 …

TCP端口崩溃,msg:socket(): Too many open files

一、现象 linux系统中运行了一个TCP服务器&#xff0c;该服务器监听的TCP端口为10000。但是长时间运行时发现该端口会崩溃&#xff0c;TCP客户端连接该端口会失败&#xff1a; 可以看到进行三次握手时&#xff0c;TCP客户端向该TCP服务器的10000端口发送了SYN报文&#xff0c;…

Qt 综合练习小项目--反金币(1/2)

目录 1 项目简介 2 项目基本配置 2.1 创建项目 2.2 添加资源 3 主场景 3.1 设置游戏主场景配置 3.2 设置背景图片 3.3 创建开始按钮 3.4 开始按钮跳跃特效实现 3.5 创建选择关卡场景 3.6 点击开始按钮进入选择关卡场景 1 项目简介 翻金币项目是一款经典的益智类游戏…