秋招Java后端开发冲刺——并发篇1(线程与进程、多线程)

一、进程

1. 进程
进程是程序的一次动态执行过程,是操作系统资源分配的基本单位。
2. 进程和线程的区别

特性进程线程
定义独立运行的程序实例,资源分配的基本单位进程中的一个执行单元,CPU调度的基本单位
资源进程拥有独立的内存空间和资源线程共享进程的堆和方法区(JDK1.8之后为元空间)
通信进程间通信(IPC)较为复杂线程间通信(共享内存)较为简单
开销创建和销毁进程开销较大创建和销毁线程开销较小
独立性进程之间相对独立线程间相互影响
并发性进程可以并发执行线程可以并发执行
调度由操作系统调度由操作系统或线程库调度
崩溃影响一个进程崩溃不会影响其他进程一个线程崩溃可能会影响整个进程

3. 进程和线程的联系

联系描述
组成关系一个进程可以包含一个或多个线程,线程是进程的一部分,多个线程共享进程的资源。
资源共享线程共享进程的堆和方法区(JDK1.8之后为元空间)、文件句柄等资源,进程则有自己的独立资源。
并发执行进程和线程都可以并发执行,利用多核 CPU 提高程序的并行度。
调度进程和线程都由操作系统进行调度,多线程程序中,线程的调度可以由 JVM 和操作系统共同管理。

二、线程

1. Java线程

  • JDK 1.2 之前,Java 线程是基于绿色线程(Green Threads)实现的,这是一种用户级线程(用户线程);JDK1.2之后Java线程是基于原生线程(Native Threads,操作系统内核线程)实现。
  • 虚拟机栈和本地方法栈线程私有是为了保证局部变量不被其他线程访问
  • 程序技术器线程私有是为了线程切换后能找到上次运行的位置继续执行

2. 线程的创建方式

  • 继承Thread类并重写 run 方法来定义线程的执行逻辑。
public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread is running");
    }

    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start();  // 启动线程
    }
}
  • 实现Runable接口并将其实例传递给 Thread 对象
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable is running");
    }

    public static void main(String[] args) {
        Thread thread = new Thread(new MyRunnable());
        thread.start();  // 启动线程
    }
}
  • 实现Callable接口并使用 FutureTask 包装 Callable 对象,然后将其传递给 Thread 对象(Callable 可以有返回值,且可以抛出异常
public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "Callable is running";
    }

    public static void main(String[] args) {
        MyCallable callable = new MyCallable();
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask);
        thread.start();  // 启动线程

        try {
            // 获取执行结果
            String result = futureTask.get();
            System.out.println(result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}
  • 其他方式:使用线程池、使用CompletableFuture类

3. 线程(Thread类)的常见方法

方法名描述
void start()启动线程,调用线程的 run 方法
void run()线程的执行方法,需要重写
void interrupt()中断线程
boolean isInterrupted()测试线程是否已经中断
static boolean interrupted()测试当前线程是否已经中断,并清除当前线程的中断状态
void join()等待线程终止
void join(long millis)等待线程终止最长时间为 millis 毫秒
void join(long millis, int nanos)等待线程终止最长时间为 millis 毫秒加 nanos 纳秒
static void sleep(long millis)使当前线程睡眠(暂停执行)指定的毫秒数
static void sleep(long millis, int nanos)使当前线程睡眠(暂停执行)指定的毫秒数加纳秒数
void setPriority(int newPriority)更改线程的优先级
int getPriority()返回线程的优先级
void setName(String name)更改线程名称
String getName()返回线程名称
long getId()返回线程的唯一标识符
Thread.State getState()返回线程的状态
boolean isAlive()测试线程是否还活着
static void yield()暂停当前正在执行的线程对象,并执行其他线程
static Thread currentThread()返回对当前正在执行的线程对象的引用
static int activeCount()返回当前线程的线程组中活动线程的数目
static void dumpStack()将当前线程的堆栈跟踪打印到标准错误流
StackTraceElement[] getStackTrace()返回一个数组,表示该线程的堆栈转储
static boolean holdsLock(Object obj)当且仅当当前线程在指定的对象上保持监视器锁时,返回 true
void setDaemon(boolean on)将该线程标记为守护线程或用户线程
boolean isDaemon()测试该线程是否为守护线程
void checkAccess()判断当前运行的线程是否有权限修改此线程
ThreadGroup getThreadGroup()返回该线程所属的线程组

4. 线程的生命周期

  • New (新建状态):线程对象被创建,但还未调用 start() 方法。
  • Runnable (就绪状态):start() 方法被调用,线程进入就绪状态,等待 CPU 时间片的分配。
  • Running (运行状态):线程获得 CPU 时间片,开始执行 run() 方法中的代码。
  • Blocked (阻塞状态):线程因等待资源或锁而进入阻塞状态,无法继续执行。
  • Waiting (等待状态):线程等待另一个线程显式地唤醒自己,通过 wait()、join() 或 sleep() 等方法进入等待状态。
  • Timed Waiting (计时等待状态):线程等待一定时间后会被自动唤醒,通过 sleep(long millis)、wait(long timeout) 或 join(long millis) 等方法进入计时等待状态。
  • Terminated (终止状态):线程运行结束或因异常退出 run() 方法,线程进入终止状态
    以下是线程生命周期的图解:
    (1)JDK1.5之前

    (2)JDK1.5之后
    在这里插入图片描述

三、多线程

1. 线程安全问题的解决方式

(1)产生原因

  • 共享资源:多个线程同时访问和修改同一资源,例如变量、对象、文件等。
  • 缺乏同步:线程在访问共享资源时,没有正确使用同步机制,导致多个线程同时执行对共享资源的操作。
  • 原子性操作的缺乏:对共享资源的操作需要分多个步骤完成,如果这些步骤不能保证原子性,会导致线程安全问题。
  • 可见性问题:一个线程对共享资源的修改,其他线程不能立即看到,导致数据不一致。
  • 指令重排序:编译器和处理器为了优化性能,可能会对指令进行重排序,导致线程安全问题

(2)解决方式

  • Synchronized关键字:同步块可以确保在同一时间只有一个线程执行同步代码,从而避免多个线程同时访问共享资源的问题。(详解请参考)
public class SynchronizedExample {

    public synchronized void synchronizedMethod() {
        // 同步实例方法
        // 其他线程不能同时执行此方法
    }

    public static synchronized void staticSynchronizedMethod() {
        // 同步静态方法
        // 其他线程不能同时执行此静态方法
    }

    public void synchronizedBlock() {
        synchronized (this) {
            // 同步代码块
            // 锁定当前实例对象
        }
    }
}
  • Lock锁:是 Java 提供的一种显式锁机制,有多种锁类型实现。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class LockExample {
    private final Lock lock = new ReentrantLock();

    public void lockMethod() {
        lock.lock();
        try {
            // 临界区代码
            // 其他线程不能同时执行此代码
        } finally {
            lock.unlock();
        }
    }
}
  • 使用线程本地变量 (ThreadLocal):每个线程都有自己的变量副本,互不干扰。
public class ThreadLocalExample {
    private ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(() -> 0);

    public void increment() {
        threadLocal.set(threadLocal.get() + 1);
    }

    public int get() {
        return threadLocal.get();
    }
}
  • Atomic Variables(原子变量):java.util.concurrent.atomic 包提供了多种原子变量,如 AtomicInteger、AtomicLong、AtomicReference 等,它们提供了一种无锁的线程安全机制。
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicExample {
    private final AtomicInteger counter = new AtomicInteger(0);

    public void increment() {
        counter.incrementAndGet();
    }

    public int getCounter() {
        return counter.get();
    }
}

(3) Synchronized 和 Lock 的区别

特性synchronizedLock
实现内置语言特性通过 java.util.concurrent.locks 包提供
锁的释放自动释放:线程退出同步代码块或方法时自动释放需要显式调用 unlock() 方法
灵活性灵活性较低,只能锁定方法或代码块灵活性较高,可以尝试获取锁、定时获取锁等
锁的获取线程阻塞式等待支持阻塞式、非阻塞式、定时尝试获取锁
性能较低:适用于简单的同步较高:适用于复杂的并发控制
条件变量提供 Condition 类,支持多个条件变量
中断响应不支持线程中断支持线程中断,响应中断请求
读写锁不支持支持,通过 ReentrantReadWriteLock 实现
公平锁不支持支持公平锁,通过 ReentrantLock 实现

2. 死锁
多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。

(1)死锁的产生:四个必要条件

  • 非抢占式:线程已获得的资源在未使用完之前不能被其他线程强行剥夺
  • 循环等待:若干线程之间形成一种头尾相接的循环等待资源关系
  • 互斥条件:该资源任意一个时刻只由一个线程占用
  • 请求与保持条件:个线程因请求资源而阻塞时,对已获得的资源保持不放

(2)死锁的预防:破坏必要条件

  • 破坏请求与保持条件:一次性申请所有的资源(会造成内存开销极大,因为程序可能很长一段时间使用不到该资源)。
  • 破坏非抢占式条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它占有的资源。
  • 破坏循环等待条件:按某一顺序申请资源,释放资源则反序释放。

(3)死锁的避免

  • 在资源分配时,借助于算法(比如银行家算法)对资源分配进行计算评估,使其进入安全状态。
  • 安全状态:系统能够按照某种线程推进顺序(P1、P2、P3……Pn)来为每个线程分配所需资源,直到满足每个线程对资源的最大需求,使每个线程都可顺利完成。
  • 银行家算法代码实现
import java.util.Arrays;

public class BankersAlgorithm {
    private int numProcesses;
    private int numResources;
    private int[] available;
    private int[][] maximum;
    private int[][] allocation;
    private int[][] need;

    public BankersAlgorithm(int numProcesses, int numResources) {
        this.numProcesses = numProcesses;
        this.numResources = numResources;
        this.available = new int[numResources];
        this.maximum = new int[numProcesses][numResources];
        this.allocation = new int[numProcesses][numResources];
        this.need = new int[numProcesses][numResources];
    }

    public void setAvailable(int[] available) {
        System.arraycopy(available, 0, this.available, 0, numResources);
    }

    public void setMaximum(int process, int[] max) {
        System.arraycopy(max, 0, this.maximum[process], 0, numResources);
        for (int j = 0; j < numResources; j++) {
            this.need[process][j] = this.maximum[process][j] - this.allocation[process][j];
        }
    }

    public void setAllocation(int process, int[] alloc) {
        System.arraycopy(alloc, 0, this.allocation[process], 0, numResources);
        for (int j = 0; j < numResources; j++) {
            this.need[process][j] = this.maximum[process][j] - this.allocation[process][j];
        }
    }

    public boolean requestResources(int process, int[] request) {
        // Step 1: Check if request <= need
        for (int j = 0; j < numResources; j++) {
            if (request[j] > need[process][j]) {
                return false; // Request exceeds need
            }
        }

        // Step 2: Check if request <= available
        for (int j = 0; j < numResources; j++) {
            if (request[j] > available[j]) {
                return false; // Request exceeds available resources
            }
        }

        // Step 3: Pretend to allocate requested resources
        for (int j = 0; j < numResources; j++) {
            available[j] -= request[j];
            allocation[process][j] += request[j];
            need[process][j] -= request[j];
        }

        // Step 4: Check system safety
        if (checkSafety()) {
            return true; // Safe state, allocation is successful
        } else {
            // Revert allocation if not safe
            for (int j = 0; j < numResources; j++) {
                available[j] += request[j];
                allocation[process][j] -= request[j];
                need[process][j] += request[j];
            }
            return false; // Not a safe state
        }
    }

    private boolean checkSafety() {
        boolean[] finish = new boolean[numProcesses];
        int[] work = Arrays.copyOf(available, numResources);

        while (true) {
            boolean foundProcess = false;

            for (int i = 0; i < numProcesses; i++) {
                if (!finish[i]) {
                    boolean canAllocate = true;
                    for (int j = 0; j < numResources; j++) {
                        if (need[i][j] > work[j]) {
                            canAllocate = false;
                            break;
                        }
                    }

                    if (canAllocate) {
                        for (int j = 0; j < numResources; j++) {
                            work[j] += allocation[i][j];
                        }
                        finish[i] = true;
                        foundProcess = true;
                    }
                }
            }

            if (!foundProcess) {
                break;
            }
        }

        for (boolean f : finish) {
            if (!f) {
                return false; // System is not in a safe state
            }
        }
        return true; // System is in a safe state
    }

    public static void main(String[] args) {
        int numProcesses = 5;
        int numResources = 3;

        BankersAlgorithm ba = new BankersAlgorithm(numProcesses, numResources);

        ba.setAvailable(new int[]{10, 5, 7});

        ba.setMaximum(0, new int[]{7, 5, 3});
        ba.setMaximum(1, new int[]{3, 2, 2});
        ba.setMaximum(2, new int[]{9, 0, 2});
        ba.setMaximum(3, new int[]{2, 2, 2});
        ba.setMaximum(4, new int[]{4, 3, 3});

        ba.setAllocation(0, new int[]{0, 1, 0});
        ba.setAllocation(1, new int[]{2, 0, 0});
        ba.setAllocation(2, new int[]{3, 0, 2});
        ba.setAllocation(3, new int[]{2, 1, 1});
        ba.setAllocation(4, new int[]{0, 0, 2});

        int[] request = {1, 0, 2};
        int process = 1;

        boolean success = ba.requestResources(process, request);

        System.out.println("Request " + (success ? "granted" : "denied"));
    }
}

3. 线程池
(1)核心参数

  • 核心线程数 (corePoolSize):线程池中保持活动的最小线程数量,即使这些线程处于空闲状态。
  • 最大线程数 (maximumPoolSize):线程池中允许的最大线程数量。当任务队列已满且已达到核心线程数时,线程池会创建新的线程来处理任务,直到达到最大线程数。达到最大线程数后,新任务将被拒绝处理,并根据饱和策略进行处理。
  • 空闲线程存活时间 (keepAliveTime):当线程池中线程数量超过核心线程数时,多余的空闲线程在等待新任务时的最长存活时间。超过这个时间的空闲线程将被终止和移除,直到线程池中的线程数量等于核心线程数。
  • 时间单位 (unit):空闲线程存活时间的单位,如秒、毫秒等。与 keepAliveTime 参数一起使用。
  • 任务队列 (workQueue):用于保存等待执行任务的阻塞队列。常用的队列实现有:
    • 直接提交队列 (SynchronousQueue):不保存任务,每个插入操作必须等待相应的删除操作。
    • 有界队列 (ArrayBlockingQueue):有固定容量的队列,当队列满时,插入操作将被阻塞。
    • 无界队列 (LinkedBlockingQueue):队列大小没有上限,理论上可以无限制地增加队列长度。
    • 优先队列 (PriorityBlockingQueue):按任务优先级排序的无界队列。
  • 线程工厂 (threadFactory):用于创建新线程的工厂。通过自定义线程工厂,可以为每个新线程设置名称、优先级等属性。
  • 拒绝策略 (handler):当任务队列已满且线程池中的线程数量已达到最大线程数时,如何处理新任务。Java 提供了四种预定义的拒绝策略:
    • AbortPolicy(默认):抛出 -
    • RejectedExecutionException,拒绝任务。
    • CallerRunsPolicy:由调用线程处理该任务。
    • DiscardPolicy:丢弃无法处理的任务,不予处理。
    • DiscardOldestPolicy:丢弃最早添加到队列中的任务,然后尝试重新提交新任务。

(2)线程池创建

  • 固定大小的线程池 (Fixed Thread Pool)
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(4);
  • 单线程化的线程池 (Single Thread Executor)
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
  • 缓存的线程池 (Cached Thread Pool)
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
  • 定时任务线程池 (Scheduled Thread Pool)
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(4);

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【基于R语言群体遗传学】-4-统计建模与算法(statistical tests and algorithm)

之前的三篇博客&#xff0c;我们对于哈代温伯格遗传比例有了一个全面的认识&#xff0c;没有看的朋友可以先看一下前面的博客&#xff1a; 群体遗传学_tRNA做科研的博客-CSDN博客 1.一些新名词 &#xff08;1&#xff09;Algorithm: A series of operations executed in a s…

软件防查盗版(慎重阅览)

在数字化日益深入的今天&#xff0c;企业运营离不开各类软件的支持。然而&#xff0c;出于成本考虑或其他原因&#xff0c;一些企业可能选择使用盗版软件。然而&#xff0c;随着版权意识的提升和法律法规的完善&#xff0c;企业使用盗版软件的风险也日益增大。为了应对这一挑战…

接口参数化-建立动态参数

接口用例需要-生成动态参数&#xff0c;接口请求参数需要动态参数时&#xff0c;在代码中写规则&#xff0c;然后用这些规则去使用 配置pom文件 新增包data/新增类名testdata 看源码 继承了一个抽象类&#xff0c;这个类被私有了&#xff0c;不能进行实例化 下方是普通方法…

NSSCTF-Web题目22(弱比较、数组绕过)

目录 [鹤城杯 2021]Middle magic 1、题目 2、知识点 3、思路 [WUSTCTF 2020]朴实无华 4、题目 5、知识点 6、思路 [鹤城杯 2021]Middle magic 1、题目 2、知识点 代码审计&#xff0c;弱比较、数组绕过 3、思路 打开题目&#xff0c;出现源代码&#xff0c;我们进行审…

OpenGL3.3_C++_Windows(27)

法线/凹凸贴图 如何让纹理产生更细节的效果&#xff0c;产生凹凸视觉感&#xff1f;解决思路之一&#xff1a;镜面贴图(黑—白&#xff09;&#xff08;&#xff08;diffuse贴图&#xff08;rgba&#xff09;&#xff09;&#xff0c;阻止部分表面被照的更亮&#xff0c;但这并…

二叉树的前中后序遍历(递归法、迭代法)leetcode144、94/145

leetcode144、二叉树的前序遍历 给你二叉树的根节点 root &#xff0c;返回它节点值的 前序 遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出&#xff1a;[1,2,3] 示例 2&#xff1a; 输入&#xff1a;root [] 输出&#xff1a;[] 示例 3&#xff1a; 输…

第T3周:天气识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 一、前期工作 本文将采用CNN实现多云、下雨、晴、日出四种天气状态的识别。较上篇文章&#xff0c;本文为了增加模型的泛化能力&#xff0c;新增了Dropout层并…

持续直击WCCI 2024:金耀初教授、台湾省台北分会等获殊荣 横滨夜景美不胜收

持续直击WCCI 2024&#xff1a;金耀初教授、台湾省台北分会等获殊荣&#xff01;横滨夜景美不胜收&#xff01; 会议之眼 快讯 会议介绍 IEEE WCCI&#xff08;World Congress on Computational Intelligence&#xff09;2024&#xff0c;即2024年IEEE世界计算智能大会&…

金融科技企业的数据治理与合规挑战:平衡创新与监管的关键战役

在当今数字化浪潮汹涌的时代&#xff0c;金融科技企业如雨后春笋般崛起&#xff0c;以其创新的技术和服务模式为金融行业带来了前所未有的变革。然而&#xff0c;伴随着业务的快速发展&#xff0c;数据治理与合规挑战也日益凸显&#xff0c;成为了金融科技企业必须直面的关键问…

Java房屋租赁管理系统附论文

作者介绍&#xff1a;计算机专业研究生&#xff0c;现企业打工人&#xff0c;从事Java全栈开发 主要内容&#xff1a;技术学习笔记、Java实战项目、项目问题解决记录、AI、简历模板、简历指导、技术交流、论文交流&#xff08;SCI论文两篇&#xff09; 上点关注下点赞 生活越过…

Python高速下载及安装的十大必备事项与C++联调

选择正确的版本&#xff1a; 访问Python官网&#xff08;https://www.python.org/&#xff09;下载最新稳定版本&#xff0c;目前最新稳定版本为3.12.4 避免下载并安装Python 2.x版本&#xff0c;因为它已经停止维护。 选择适合操作系统的安装包&#xff1a; 根据你的操作系…

IPFoxy Tips:为什么要选择动态住宅代理IP?

在大数据时代的背景下&#xff0c;代理IP成为了很多企业顺利开展的重要工具。代理IP地址可以分为住宅代理IP地址和数据中心代理IP地址。选择住宅代理IP的好处是可以实现真正的高匿名性&#xff0c;而使用数据中心代理IP可能会暴露自己使用代理的情况。 住宅代理IP是指互联网服务…

一场别开生面的python应用实战案例

学好python&#xff0c;改变人生&#xff01; 最近看了央视旗下的玉渊潭天微博介绍了菲律宾control我们sina微博的视频&#xff0c;这是一个难得的python实战案例&#xff0c;至少有四五个python重要硬核方向值得研究&#xff0c;所以今天写一下这个相关的一些技术领域&#xf…

Redis持久化的三种方式(RDB、AOF和混合)

Redis持久化的三种方式(RDB、AOF和混合) 目录 Redis持久化的三种方式(RDB、AOF和混合)介绍RDB示例1.配置文件2.触发 RDB 快照保存3.验证 AOF示例1.配置文件2.校验 混合型持久化存储配置文件 介绍 Redis数据主要存储与内存中&#xff0c;因此如果服务器意外重启、宕机、崩溃&am…

elementui中@click短时间内多次触发,@click重复点击,做不允许重复点击处理

click快速点击&#xff0c;发生多次触发 2.代码示例&#xff1a; //html<el-button :loading"submitLoading" type"primary" click"submitForm">确 定</el-button>data() {return {submitLoading:false,}}//方法/** 提交按钮 */sub…

页面替换菜单栏图标

图标素材库&#xff1a;https://www.iconfont.cn/?spma313x.collections_index.i3.2.51703a81hOhc8B 1、找到自己喜欢的图标下载svg 2、添加到icons中 3、在components中创建对应的vue页面添加对应图标svg中代码 4、在router中引入 5、在对应的菜单下使用图标

复旦大学:一个小技巧探测大模型的知识边界,有效消除幻觉

孔子说“知之为知之&#xff0c;不知为不知&#xff0c;是知也”&#xff0c;目前的大模型非常缺乏这个能力。虽然大模型拥有丰富的知识&#xff0c;但它仍然缺乏对自己知识储备的正确判断。近年来LLMs虽然展现了强大的能力&#xff0c;但它们偶尔产生的内容捏造&#xff0c;即…

基于改进YOLOv5s的跌倒行为检测 | 引入SKAttention注意机制 + 引入空间金字塔池化结构SPPFCSPC + 结合ASFF自适应空间融合

前言&#xff1a;Hello大家好&#xff0c;我是小哥谈。为了实现电厂人员跌倒行为的实时检测&#xff0c;防止跌倒昏迷而无法及时发现并救援的事件发生&#xff0c;针对跌倒行为检测实时性以及特征提取能力不足的问题&#xff0c;提出了一种改进YOLOv5s的跌倒行为检测算法网络&a…

MySQL期末答辩—仓库管理系统

仓库管理系统&#xff1a;仓库管理系统是一种基于互联网对实际仓库的管理平台&#xff0c;旨在提供一个方便、快捷、安全的存取货物和查询商品信息平台。该系统通过在线用户登录查询&#xff0c;可以线上操作线下具体出/入库操作、查询仓库商品信息、提高仓库运作效率&#xff…

一文包学会ElasticSearch的大部分应用场合

ElasticSearch 官网下载地址&#xff1a;Download Elasticsearch | Elastic 历史版本下载地址1&#xff1a;Index of elasticsearch-local/7.6.1 历史版本下载地址2&#xff1a;Past Releases of Elastic Stack Software | Elastic ElasticSearch的安装(windows) 安装前所…