Java并发类API -- Future和Callable

news2024/9/20 16:23:47

1.Future和Callable接口

Future 是一个表示异步计算结果的接口;

接口Callable与线程功能密不可分,但和Runnable的主要区别为:
1)Callable接口的call()方法可以有返回值,而Runnable接口的run()方法没有返回值。
2)Callable接口的call()方法可以声明抛出异常,而Runnable接口的run()方法不可以声明抛出异常。
执行完Callable接口中的任务后,返回值是通过Future接口进行获得的。

接口Future中的方法

• cancel(boolean mayInterruptIfRunning): 尝试取消任务。

• isCancelled(): 检查任务是否被取消。

• isDone(): 用于判断该计算是否已经完成。

• get(): 获取任务的结果(会阻塞直到任务完成)。

• get(long timeout, TimeUnit unit): 在指定的时间内等待并获取任务结果。

1.1 Future get()+ Callable 示例:

方法submit(Callable<T>)可以执行参数为Callable的任务。
方法get()用于获得返回值。
创建类MyCallable.java代码如下:

package mycallable;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {

    private int age;

    public MyCallable(int age) {
        super();
        this.age = age;
    }

    public String call() throws Exception {
        Thread.sleep(8000);
        return "返回值 年龄是:" + age;
    }

}

创建类Run.java代码如下:

package test.run;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import mycallable.MyCallable;

public class Run {

    public static void main(String[] args) throws InterruptedException {
        try {
            MyCallable callable = new MyCallable(100);

            ThreadPoolExecutor executor = new ThreadPoolExecutor(2, 3, 5L,
                    TimeUnit.SECONDS, new LinkedBlockingDeque());
            Future<String> future = executor.submit(callable);
            System.out.println("main A " + System.currentTimeMillis());
            System.out.println(future.get());
            System.out.println("main B " + System.currentTimeMillis());
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

1.2 Future isDone()+ Callable示例

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureIsDoneExample {

    public static void main(String[] args) {
        // 创建一个线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        // 创建一个Callable任务
        Callable<String> callableTask = () -> {
            Thread.sleep(2000); // 模拟一个耗时任务
            return "Task's execution";
        };

        // 提交任务并获得Future对象
        Future<String> future = executorService.submit(callableTask);

        // 使用isDone()方法来检查任务是否完成
        while (!future.isDone()) {
            System.out.println("Task is still not done...");
            try {
                Thread.sleep(500); // 让主线程等待一段时间
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        // 任务完成后,获取结果
        try {
            String result = future.get(); // 这将阻塞直到结果可用
            System.out.println("Task completed! Result: " + result);
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }

        // 关闭线程池
        executorService.shutdown();
    }
}

代码解释:

1. 创建线程池: 使用 Executors.newSingleThreadExecutor() 创建一个单线程的线程池。

2. 创建任务: 使用 Callable 接口创建一个任务,它将在 2 秒后返回一个字符串。

3. 提交任务并获取 Future 对象: 使用 executorService.submit(callableTask) 提交任务,并获得一个表示该任务的 Future 对象。

4. 检查任务是否完成: 使用 isDone() 方法检查任务是否完成。如果任务没有完成,程序会每 500 毫秒打印一次提示信息。

5. 获取任务结果: 一旦任务完成,使用 future.get() 方法获取任务结果,并打印出来。

6. 关闭线程池: 最后,使用 executorService.shutdown() 关闭线程池。

结果输出:

• 在任务执行过程中,控制台会打印多次 “Task is still not done…”,表示任务尚未完成。

• 一旦任务完成,程序将打印任务的结果,如 “Task completed! Result: Task’s execution”。

1.3 Future cancel() 和 isCancelled()示例

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class FutureCancelExample {

    public static void main(String[] args) {
        // 创建一个线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        // 创建一个Callable任务
        Callable<String> callableTask = () -> {
            try {
                Thread.sleep(5000); // 模拟一个耗时任务
                return "Task's execution completed";
            } catch (InterruptedException e) {
                return "Task was interrupted";
            }
        };

        // 提交任务并获得Future对象
        Future<String> future = executorService.submit(callableTask);

        // 等待一段时间然后取消任务
        try {
            Thread.sleep(2000); // 主线程等待2秒
            boolean isCancelled = future.cancel(true); // 尝试取消任务

            // 检查任务是否被取消
            if (isCancelled) {
                System.out.println("Task was cancelled successfully.");
            } else {
                System.out.println("Failed to cancel the task.");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // 使用isCancelled()检查任务是否已被取消
        if (future.isCancelled()) {
            System.out.println("The task was cancelled and did not complete.");
        } else {
            try {
                // 获取任务的结果
                String result = future.get(); // 如果任务未被取消,则此方法会阻塞直到任务完成
                System.out.println("Task completed! Result: " + result);
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        }

        // 关闭线程池
        executorService.shutdown();
    }
}

代码解释:

1. 创建线程池: 使用 Executors.newSingleThreadExecutor() 创建一个单线程的线程池。

2. 创建任务: 使用 Callable 接口创建一个任务,它将在 5 秒后返回一个字符串。如果任务被中断,则返回 “Task was interrupted”。

3. 提交任务并获取 Future 对象: 使用 executorService.submit(callableTask) 提交任务,并获得一个表示该任务的 Future 对象。

4. 取消任务: 主线程等待 2 秒后,调用 future.cancel(true) 取消任务。true 参数表示如果任务正在运行,则尝试中断它。

5. 检查任务是否被取消: 使用 isCancelled() 方法检查任务是否已被取消。如果任务已被取消,程序会输出相应的消息。

6. 获取任务结果: 如果任务未被取消,使用 future.get() 方法获取任务结果。如果任务已被取消,任务不会返回结果。

7. 关闭线程池: 最后,使用 executorService.shutdown() 关闭线程池。

结果输出:

• 如果任务被成功取消,控制台会输出 “Task was cancelled successfully.” 和 “The task was cancelled and did not complete.”

• 如果任务未被取消(可能因为任务已经完成),程序会输出 “Failed to cancel the task.”,并打印任务的结果。

这个示例展示了如何使用 cancel() 方法来尝试取消正在执行的任务,以及如何使用 isCancelled() 方法来检查任务是否已经取消。

1.4 Future get(long timeout,TimeUnit unit)示例

Future 接口的 get(long timeout, TimeUnit unit) 方法用于在指定的超时时间内获取异步任务的结果。如果在超时时间内任务没有完成,则抛出 TimeoutException。以下是一个示例,展示了如何使用该方法:

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

public class FutureGetWithTimeoutExample {

    public static void main(String[] args) {
        // 创建一个线程池
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        // 创建一个Callable任务
        Callable<String> callableTask = () -> {
            try {
                Thread.sleep(3000); // 模拟一个耗时任务(3秒)
                return "Task's execution completed";
            } catch (InterruptedException e) {
                return "Task was interrupted";
            }
        };

        // 提交任务并获得Future对象
        Future<String> future = executorService.submit(callableTask);

        try {
            // 尝试在2秒内获取任务的结果
            String result = future.get(2, TimeUnit.SECONDS);
            System.out.println("Task completed within timeout! Result: " + result);
        } catch (TimeoutException e) {
            System.out.println("Task did not complete within the timeout period.");
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        } finally {
            // 关闭线程池
            executorService.shutdown();
        }
    }
}

代码解释:

1. 创建线程池: 使用 Executors.newSingleThreadExecutor() 创建一个单线程的线程池。

2. 创建任务: 使用 Callable 接口创建一个任务,该任务将模拟一个耗时 3 秒的操作。如果任务被中断,则返回 “Task was interrupted”。

3. 提交任务并获取 Future 对象: 使用 executorService.submit(callableTask) 提交任务,并获得一个表示该任务的 Future 对象。

4. 使用 get(long timeout, TimeUnit unit) 获取任务结果:

• 尝试在 2 秒内获取任务的结果。如果任务在 2 秒内完成,程序将打印任务结果。

• 如果任务没有在 2 秒内完成,将抛出 TimeoutException,并打印 “Task did not complete within the timeout period.”

5. 关闭线程池: 最后,使用 executorService.shutdown() 关闭线程池。

结果输出:​​​​​​​

• 由于任务需要 3 秒才能完成,而超时时间为 2 秒,因此控制台会输出 “Task did not complete within the timeout period.”

• 如果将任务的睡眠时间改为小于或等于 2 秒,则程序将成功获取任务结果,并打印 “Task completed within timeout! Result: Task’s execution completed”。

这个示例展示了如何使用 get(long timeout, TimeUnit unit) 方法来设定获取任务结果的超时时间,以及如何处理超时情况。

2.RejectedExecutionHandler 被拒绝的任务处理

ejectedExecutionHandler 是 Java 中 java.util.concurrent 包的一部分,用于处理当任务无法被执行时的情况。这个接口通常与 ThreadPoolExecutor 一起使用,当线程池无法接受新的任务时,线程池会调用 RejectedExecutionHandler 的方法来处理这些被拒绝的任务。

主要使用场景:

当一个 ThreadPoolExecutor 已经达到了其最大线程数并且其任务队列也已经满了的情况下,新的任务将无法被接受。这时,就会触发 RejectedExecutionHandler 来处理这些被拒绝的任务。你可以通过实现这个接口来定义自己的拒绝策略。

方法:

RejectedExecutionHandler 接口只有一个方法:

void rejectedExecution(Runnable r, ThreadPoolExecutor executor);

• r:这是被拒绝的任务,通常是一个 Runnable 对象。

• executor:这个参数是触发拒绝的 ThreadPoolExecutor 对象。

内置的拒绝策略:

Java 提供了一些内置的 RejectedExecutionHandler 实现,常见的有以下几种:

1. AbortPolicy(默认策略):抛出 RejectedExecutionException,阻止系统继续运行。

2. CallerRunsPolicy:由提交任务的线程来执行该任务,这样可以减缓任务提交的速度。

3. DiscardPolicy:直接丢弃任务,不做任何处理。

4. DiscardOldestPolicy:丢弃队列中最老的未处理任务,然后重新尝试提交新任务。

自定义 RejectedExecutionHandler 示例:

你可以实现 RejectedExecutionHandler 接口来自定义拒绝策略。例如,以下代码展示了如何自定义一个拒绝策略,将被拒绝的任务记录日志:

import java.util.concurrent.*;

public class CustomRejectedExecutionHandlerExample {

    public static void main(String[] args) {
        // 创建一个自定义的RejectedExecutionHandler
        RejectedExecutionHandler customHandler = new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                System.out.println("Task " + r.toString() + " rejected. Logging this information.");
                // 这里可以实现其他的处理逻辑,比如记录日志或者发送通知
            }
        };

        // 创建一个线程池,并设置自定义的RejectedExecutionHandler
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                2, 2, 0L, TimeUnit.MILLISECONDS,
                new LinkedBlockingQueue<>(2),
                customHandler);

        // 提交任务
        for (int i = 0; i < 10; i++) {
            executor.submit(new Task());
        }

        // 关闭线程池
        executor.shutdown();
    }

    static class Task implements Runnable {
        @Override
        public void run() {
            try {
                Thread.sleep(1000);
                System.out.println("Task completed: " + Thread.currentThread().getName());
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }

        @Override
        public String toString() {
            return "Task@" + hashCode();
        }
    }
}

代码解释:

1. 自定义 RejectedExecutionHandler 实现了 RejectedExecutionHandler 接口,定义了一个简单的处理逻辑,当任务被拒绝时,会输出一条日志信息。

2. 创建 ThreadPoolExecutor 使用自定义的 RejectedExecutionHandler 初始化 ThreadPoolExecutor,并设置核心线程数和最大线程数均为 2,任务队列的容量为 2。

3. 提交任务: 提交 10 个任务,由于线程池和队列容量有限,多余的任务将被拒绝,并触发自定义的拒绝处理逻辑。

输出:

由于线程池和任务队列总共只能处理 4 个任务,其余 6 个任务将被拒绝,输出类似于以下内容:

Task Task@XXXXXX rejected. Logging this information.
Task completed: pool-1-thread-1
Task completed: pool-1-thread-2

3. execute()和 submit()区别

execute() 和 submit() 是 Java 中用于将任务提交给线程池的两种方法,二者在功能和用法上有一些重要区别。下面详细解释它们的区别:

execute() 方法

execute() 是 Executor 接口中定义的方法,用于提交一个 Runnable 任务给线程池执行。

主要特点:

1. 无返回值: execute() 方法没有返回值。它只负责将任务提交给线程池执行,不提供任务执行完成后的任何反馈。

2. 适用于 Runnable execute() 方法只能接受实现了 Runnable 接口的任务,因此不支持返回执行结果的任务。

3. 异常处理: 如果 Runnable 任务在执行过程中抛出异常且未被捕获,异常将直接传播,可能导致线程池中的线程终止。

示例:

ExecutorService executor = Executors.newFixedThreadPool(2);
executor.execute(() -> {
    System.out.println("Task executed using execute()");
});
executor.shutdown();

submit() 方法

submit() 是 ExecutorService 接口中定义的方法,用于提交一个 Runnable 或 Callable 任务,并返回一个 Future 对象。

主要特点:

1. 返回 Future 对象: submit() 方法返回一个 Future 对象,通过它可以获取任务的执行结果、检查任务是否完成、取消任务等。

2. 支持 RunnableCallable submit() 方法可以接受 Runnable 和 Callable 两种类型的任务:

• 对于 Runnable 任务,submit() 返回一个 Future,但是 Future.get() 方法返回的结果是 null,因为 Runnable 不会返回执行结果。

• 对于 Callable 任务,submit() 返回一个 Future,可以通过 Future.get() 获取 Callable 的返回值。

3. 异常处理: 通过 submit() 提交的任务,如果在执行过程中抛出异常,异常不会直接传播到调用线程。相反,异常会被捕获并存储在 Future 对象中,可以通过调用 Future.get() 来获取该异常。

ExecutorService executor = Executors.newFixedThreadPool(2);

// 使用submit提交Runnable任务
Future<?> future1 = executor.submit(() -> {
    System.out.println("Task executed using submit() with Runnable");
});

// 使用submit提交Callable任务
Future<String> future2 = executor.submit(() -> {
    return "Task executed using submit() with Callable";
});

// 获取Callable任务的结果
try {
    String result = future2.get();
    System.out.println("Callable task result: " + result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

executor.shutdown();

总结区别:​​​​​​​

返回值: execute() 不返回结果,submit() 返回一个 Future 对象,可以通过 Future 获取任务的执行结果。

任务类型: execute() 只接受 Runnable 任务,submit() 可以接受 Runnable 和 Callable 任务。

异常处理: execute() 提交的任务如果抛出异常,异常会直接传播。方法execute()在默认的情况下异常直接抛出,不能捕获,但可以通过自定义Thread-Factory的方式进行捕获,而submit()方法在默认的情况下,可以catch Execution-Exception捕获异常并可以通过 Future.get() 进行处理。

4.验证Future的缺点

接口Future的实现类是FutureTask.java,而且在使用线程池时,默认的情况下也是使用FutureTask.java类作为接口Future的实现类,也就是说,如果在使用Future与Callable的情况下,使用Future接口也就是在使用FutureTask.java类。
Callable接口与Runnable接口在对比时主要的优点是,Callable接口可以通过Future取得返回值。但需要注意的是,Future接口调用get()方法取得处理的结果值时是阻塞性的,也就是如果调用Future对象的get()方法时,任务尚未执行完成,则调用get()方法时一直阻塞到此任务完成时为止。如果是这样的效果,则前面先执行的任务一旦耗时很多,则后面的任务调用get()方法就呈阻塞状态,也就是排队进行等待,大大影响运行效率。也就是主线程并不能保证首先获得的是最先完成任务的返回值,这就是Future的缺点,影响效率。
创建验证用的项目,名称为futureLast,类MyCallable.java代码如下:

package mycallable;

import java.util.concurrent.Callable;

public class MyCallable implements Callable<String> {

    private String username;
    private long sleepValue;

    public MyCallable(String username, long sleepValue) {
        super();
        this.username = username;
        this.sleepValue = sleepValue;
    }

    @Override
    public String call() throws Exception {
        System.out.println(username);
        Thread.sleep(sleepValue);
        return "return " + username;
    }

}

类Test.java代码如下:

package test;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

import mycallable.MyCallable;

public class Test {

    public static void main(String[] args) {
        try {
            MyCallable callable1 = new MyCallable("username1", 5000);
            MyCallable callable2 = new MyCallable("username2", 4000);
            MyCallable callable3 = new MyCallable("username3", 3000);
            MyCallable callable4 = new MyCallable("username4", 2000);
            MyCallable callable5 = new MyCallable("username5", 1000);

            List<Callable> callableList = new ArrayList<Callable>();
            callableList.add(callable1);
            callableList.add(callable2);
            callableList.add(callable3);
            callableList.add(callable4);
            callableList.add(callable5);

            List<Future> futureList = new ArrayList<Future>();

            ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 5,
                    TimeUnit.SECONDS, new LinkedBlockingDeque<Runnable>());
            for (int i = 0; i < 5; i++) {
                futureList.add(executor.submit(callableList.get(i)));
            }
            System.out
                    .println("run first time=  " + System.currentTimeMillis());
            for (int i = 0; i < 5; i++) {
                System.out.println(futureList.get(i).get() + " "
                        + System.currentTimeMillis());
            }
            // 按顺序打印的效果
            // 说明一个Future对应指定的一个Callable
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }

    }
}

程序运行结果如图:

方法get()呈阻塞状态,这就是Future接口的缺点,根据这个特性,JDK1.5提供了CompletionService接口可以解决这个问题. 

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

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

相关文章

java:IDEA修改java版本的几个不同的地方

文章目录 项目JDK设置&#xff08;Project SDK&#xff09;项目模块级JDK设置&#xff08;Module SDK&#xff09;IDE级别的JDK设置Maven配置文件编译器&#xff08;Java Compiler&#xff09;构建工具配置文件&#xff08;如build.gradle或pom.xml&#xff09;.idea/misc.xml文…

C语言(17)——单链表的应用

目录 1.单链表经典算法OJ题⽬ 1.1单链表相关经典算法OJ题1&#xff1a;移除链表元素 1.2单链表相关经典算法OJ题2&#xff1a;反转链表 1.3 单链表相关经典算法OJ题3&#xff1a;链表的中间节点 1.单链表经典算法OJ题⽬ 1.1单链表相关经典算法OJ题1&#xff1a;移除链表元素…

还有比这java状态压缩更通俗易懂的解释?

前言 Java中的状态压缩&#xff0c;或者说位运算状态压缩&#xff0c;是一种利用位操作&#xff08;如位与&、位或|、位异或^、位非~、左移<<、右移>>等&#xff09;来高效地存储和处理状态信息的技术。这种技术特别适用于那些状态空间不是很大&#xff0c;但…

微服务:网关路由和登录校验

续上篇&#xff1a;微服务&#xff1a;服务的注册与调用和OpenFiegn-CSDN博客 参考&#xff1a;黑马程序员之微服务 &#x1f4a5; 该系列属于【SpringBoot基础】专栏&#xff0c;如您需查看其他SpringBoot相关文章&#xff0c;请您点击左边的连接 目录 一、网关路由 1. 网关…

苹果注册海外账户|注册海外Apple ID|下载海外App

注册海外apple ID 背景 先前的一直使用的Apple ID注册地区是国内。 但是一些app因地区限制需要海外账户才能下载&#xff0c;因此需要使用海外apple ID。 因此需要注册海外apple ID&#xff08;如美国&#xff09;。 前言 绑定手机号 手机号可以和国内Apple ID一样没关系…

记一次SATA硬盘上电不转问题排查(最终查到和供电线有关)

一、背景 今年把旧的台式机换成了新的台式机&#xff0c;把硬盘挪到新电脑了。 二、问题 把硬盘挪到新电脑后&#xff0c;SSD可以正常使用&#xff0c;但是有个氦气机械盘始终不能用。 三、排查过程 对比实验 做了一些实验&#xff1a; 把硬盘接回旧电脑会正常转&#x…

【轨物洞见】找到技术创新的“真问题”

技术创新是用技术解决真问题&#xff0c;创造真价值。问题是需求&#xff0c;技术是供应&#xff0c;将需求与供应有效连接的能力&#xff0c;是创新力。技术创新的第一步在于找到“真问题”。在找问题之前&#xff0c;我们先了解一下问题是如何产生的。 问题就是期望值与现状之…

eNSP 华为交换机链路聚合

华为交换机链路聚合 链路聚合好处&#xff1a; 1、提高带宽 2、链路冗余 SW_2&#xff1a; <Huawei>sys [Huawei]sys SW_2 [SW_2]vlan batch 10 20 [SW_2]int g0/0/4 [SW_2-GigabitEthernet0/0/4]port link-type access [SW_2-GigabitEthernet0/0/4]port default vl…

ECCV2024|商汤发布3D面部动画系统UniTalker:通过统一模型扩展音频驱动的 3D 面部动画

商汤研究院最新发布了一个先进的3D面部动画系统UniTalker&#xff0c;可以从不同的音频领域生成逼真的面部动作&#xff0c;包括各种语言的清晰和嘈杂的声音、文本到语音生成的音频&#xff0c;甚至伴有背景音乐的嘈杂歌曲。 UniTalker 可以输出多个注释。对于具有新注释的数据…

理解线程id和简单封装原生线程库

一、理解线程id 首先我们要知道给用户提供的线程id不是内核里面LWP&#xff08;轻量级进程id&#xff09;&#xff0c;而是pthread库自己维护的一个唯一值。 我们理解为什么线程id不是内核里面LWP&#xff0c;因为用户没有权限使用内核里面的字段&#xff0c;那是专门给OS管理…

DOM破坏案例

目录 DOM破坏 编码问题 简单闭合""号使用onclick onclick函数 焦点事件 标签使用 限制数字字母 js匿名函数绕过 覆盖 DOM破坏 编码问题 urlcode可以被识别 %16进制 <textarea> 可以解码不能执行 <script>&#59</script> 没有实体编码…

基于asp.net的在线考试系统、基于c#的在线考试管理系统

摘 要 伴随着社会以及科学技术的发展&#xff0c;互联网已经渗透在人们的身边&#xff0c;网络慢慢的变成了人们的生活必不可少的一部分&#xff0c;紧接着网络飞速的发展&#xff0c;管理系统这一名词已不陌生&#xff0c;越来越多的学校、公司等机构都会定制一款属于自己个…

SpingBoot自动装配原理

一&#xff0c;什么是SpringBoot自动装配 springboot自动装配&#xff1a;一个springboot项目通过扫描类路径下存在的类和各种配置信息自动装配&#xff0c;生成对应的B哦按对象&#xff0c;然后将他们交给spring容器管理。 二、SpringBoot自动装配原理 2.1启动类注解SpringB…

在MAVEN中版本依赖有冲突改怎么处理

1.为什么会出现版本依赖的冲突 如果存在版本冲突&#xff0c;通常可能会引发的报错是ClassNotFoundException、NoSuchMethodError等错误。Maven依赖版本冲突通常是由于间接依赖导致同一个jar包存在多个不同版本。例如&#xff0c;如果B依赖了A的1.0版本&#xff0c;而C依赖了A…

Nginx--防盗链问题

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 一、什么是盗链 盗链是一种网络行为&#xff0c;指的是一个网站未经授权&#xff0c;直接使用另一个网站资源&#xff08;如图片、视频、音乐、文件等…

人人可以做的RAG检索增强生成实战

AI大模型持续火爆&#xff0c;我作为IT人员从业者也想参与一下&#xff0c;但是奈何大模型的参数太大&#xff0c;动辄上亿的参数需要很大的算力&#xff0c;GPU卡必不可少&#xff0c;对于手头羞涩的小白&#xff0c;不想投几千大洋只为了满足自己的好奇心。这真是个难题。 现…

【车载开发系列】单片机烧写的文件

【车载开发系列】单片机烧写的文件 【车载开发系列】单片机烧写的文件 【车载开发系列】单片机烧写的文件一. 什么是bin二. 什么是Hex三. 什么是Motorola S-record&#xff08;S19&#xff09;四. ELF格式五. Bin与Hex文件的比对六. 单片机烧写文件的本质 一. 什么是bin bin是…

回顾 | 瑞云科技亮相ICIC2024,虚拟仿真实训云平台引关注

2024年8月7日&#xff0c;天津市虚拟仿真学会主办的第二十届智能计算国际会议&#xff08;ICIC2024&#xff09;——虚拟仿真技术交流平行会议暨天津市虚拟仿真学会2024年暑期技术交流会在天津盛大召开。本次大会汇聚来自全国的顶尖专家、学者和行业领袖&#xff0c;共同探讨虚…

Notepad--文本编辑工具 for Mac教程【苹果电脑-简单轻松上手-免费Mac软件推荐】

Mac分享吧 文章目录 效果一、下载软件二、开始安装1、双击运行软件&#xff0c;将其拖入应用程序中&#xff0c;等待安装完毕2、应用程序显示软件图标&#xff0c;表示安装成功 三、运行测试解决“软件已损坏&#xff0c;无法打开”问题&#xff0c;若没有该问题&#xff0c;可…

TI DSP TMS320F280025 Note1:工程模板Template创建

TMS320F280025工程模板Template创建 文章目录 TMS320F280025工程模板Template创建新建一个ccs工程为需要添加的一些文件新建文件夹从c2000中拷贝官方基础文件到工程中cmd链接文件common头文件和源文件headers头文件和源文件库函数文件添加driverlib.lib文件 添加文件的路径文件…