java多线程文件下载器

news2024/10/6 18:27:25

文章目录

  • 1.简介
  • 2.文件下载的核心
  • 3.文件下载器的基础代码
    • 3.1 HttpURLConnection
    • 3.2 用户标识
  • 4.下载信息
    • 4.1 计划任务
    • 4.2 ScheduledExecutorService
      • 🍀 schedule方法
      • 🍀 scheduleAtFixedRate方法
      • 🍀 scheduleWithFixedDelay方法
  • 5.线程池简介
    • 5.1 ThreadPoolExecutor 构造方法参数
    • 5.2 线程池工作过程
    • 5.3 线程池的状态
    • 5.4 线程池的关闭
    • 5.5 工作队列
  • 6.代码实现
    • 6.1 环境搭建
      • 🍀 基本信息
      • 🍀 创建项目
    • 6.2 实现逻辑
    • 6.3 项目结构
    • 6.4 类代码
      • 🍀 constant 包
        • 📌 Constant
      • 🍀 util 包
        • 📌 FileUtils
        • 📌 HttpUtils
        • 📌 LogUtils
      • 🍀 core 包
        • 📌 DownloadInfoThread
        • 📌 DownloaderTask
        • 📌 Downloader
      • 🍀 Main 主类

1.简介

该项目应用的知识点包括:

  • RandomAccessFile 类的运用
  • HttpURLConnection 类的运用
  • 线程池的使用
  • 原子类 LongAdder 的运用
  • CountDownLatch 类的运用
  • ScheduledExecutorService 类的运用

2.文件下载的核心

从互联网下载文件有点类似于我们将本地某个文件复制到另一个目录下,也会利用 IO 流进行操作。对于从互联网下载,还需要将本地和下载文件所在的服务器建立连接。

image-20231107124520655

3.文件下载器的基础代码

3.1 HttpURLConnection

从互联网中下载文件的话,需要与文件所在的服务器建立连接,这里可以使用 jdk 提供的 java.net.HttpURLConnection 类来帮助我们完成这个操作。jdk11中有提供 java.net.http.HttpClient 类来替代 HttpURLConnection,由于现在使用的是 jdk8,因此先不用 jdk11 中的 HttpClient。除此之外还有一些其他第三方提供类可以执行类似的操作,这里就不赘述了。

3.2 用户标识

我们通过浏览器访问某个网站的时候,会将当前浏览器的版本,操作系统版本等信息的标识发送到网站所在的服务器中。当用程序代码去访问网站时,需要将这个标识发送过去。

Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1

4.下载信息

4.1 计划任务

文件下载的时候最好能够展示出下载的速度,已下载文件大小等信息。这里可以每隔一段时间来获取文件的下载信息,比如间隔 1 秒获取一次,然后将信息打印到控制台。文件下载是一个独立的线程,另外还需要再开启一个线程来间隔获取文件的信息。java.util.concurrent.ScheduledExecutorService 这个类可以帮助我们来实现此功能。

4.2 ScheduledExecutorService

在该类中提供了一些方法可以帮助开发者实现间隔执行的效果,下面列出一些常见的方法及其参数说明。我们可以通过下面方式来获取该类的对象,其中 1 标识核心线程的数量。

ScheduledExecutorService s = Executors.newScheduledThreadPool(1);

🍀 schedule方法

该方法是重载的,这两个重载的方法都是有 3 个形参,只是第一个形参不同。

参数含义
Runnable / Callable<V>可以传入这两个类型的任务
long delay延时的时间数量
TimeUnit unit时间单位

该方法的作用是让任务按照指定的时间延时执行。

🍀 scheduleAtFixedRate方法

该方法的作用是按照指定的时间延时执行,并且每隔一段时间再继续执行。

参数含义
Runnable command执行的任务
long initialDelay延时的时间数量
long period间隔的时间数量
TimeUnit unit时间单位

倘若在执行任务的时候,耗时超过了间隔时间,则任务执行结束之后直接再次执行,而不是再等待间隔时间执行。

🍀 scheduleWithFixedDelay方法

该方法的作用是按照指定的时间延时执行,并且每隔一段时间再继续执行。

参数含义
Runnable command执行的任务
long initialDelay延时的时间数量
long period间隔的时间数量
TimeUnit unit时间单位

在执行任务的时候,无论耗时多久,任务执行结束之后都会等待间隔时间之后再继续下次任务。

5.线程池简介

线程在创建,销毁的过程中会消耗一些资源,为了节省这些开销,jdk 添加了线程池。线程池节省了开销,提高了线程使用的效率。阿里巴巴开发文档中建议在编写多线程程序的时候使用线程池。

5.1 ThreadPoolExecutor 构造方法参数

在 juc 包下提供了 ThreadPoolExecutor 类,可以通过该类来创建线程池,这个类中有4个重载的构造方法,最核心的构造方法是有7个形参的,这些参数所代表的意义如下:

参数含义
corePoolSize线程池中核心线程的数量
maximumPoolSize线程池中最大线程的数量,是核心线程数量和非核心线程数量之和
keepAliveTime非核心线程空闲的生存时间
unitkeepAliveTime 的生存时间单位
workQueue当没有空闲的线程时,新的任务会加入到 workQueue 中排队等待
threadFactory线程工厂,用于创建线程
handler拒绝策略,当任务太多无法处理时的拒绝策略

5.2 线程池工作过程

image-20231108082712983

5.3 线程池的状态

状态说明
RUNNING创建线程池之后的状态是 RUNNING
SHUTDOWN该状态下,线程池就不会接收新任务,但会处理阻塞队列剩余任务,相对温和
STOP该状态下会中断正在执行的任务,并抛弃阻塞队列任务,相对暴力
TIDYING任务全部执行完毕,活动线程为 0 即将进入终止
TERMINATED线程池终止

5.4 线程池的关闭

线程池使用完毕之后需要进行关闭,提供了以下两种方法进行关闭。

方法说明
shutdown()该方法执行后,线程池状态变为 SHUTDOWN,不会接收新任务,但是会执行完已提交的任务,此方法不会阻塞调用线程的执行。
shutdownNow()该方法执行后,线程池状态变为 STOP,不会接收新任务,会将队列中的任务返回,并用 interrupt 的方式中断正在执行的任务。

5.5 工作队列

jdk 中提供的一些工作队列 workQueue。

队列说明
SynchronousQueue直接提交队列
ArrayBlockingQueue有界队列,可以指定容量
LinkedBlockingDeque无界队列
PriorityBlockingQueue优先任务队列,可以根据任务优先级顺序执行任务

6.代码实现

6.1 环境搭建

🍀 基本信息

  • 开发工具:IDEA
  • JDK 版本:8
  • 项目编码:utf-8

🍀 创建项目

在开发工具中创建一个 javase 项目即可,无需导入第三方 jar 依赖。

6.2 实现逻辑

  1. 先判断是否已存在重复文件,该步骤其实可忽略,因为最终下载合并的文件名已采用时间戳进行了唯一标识;
  2. 启动一个线程每隔一秒打印下载情况;
  3. 切分任务,多线程分快下载;
  4. 全部块文件下载完毕,合并分块文件;
  5. 合并分块文件完毕,清理分块文件;
  6. 释放资源,关闭线程池和连接对象。

6.3 项目结构

image-20231108091928709

包名作用
constant存放常量类的包
core存放了下载器核心类的包
util存放工具类的包
Main主类

6.4 类代码

🍀 constant 包

📌 Constant
/**
 * Description: 存放项目常量
 *
 * @Author 狐狸半面添
 * @Create 2023/11/6 1:22
 * @Version 1.0
 */
public class Constant {
    /**
     * 指定下载目录的存放位置
     */
    public static final String PATH = "D:\\download\\";

    public static final double MB = 1024d * 1024d;
    public static final double KB = 1024d;

    /**
     * 每次读取的字节大小
     */
    public static final int BYTE_SIZE = 1024 * 100;

    /**
     * 块文件(临时文件)的后缀
     */
    public static final String PART_FILE_SUFFIX = ".temp";

    /**
     * 线程数量
     */
    public static final int THREAD_NUM = 5;

    // 创建存放位置的代码
    // public static void main(String[] args) {
    //     File file = new File("D:\\download");
    //     if (!file.exists()) {
    //         file.mkdir();
    //     }
    // }
}

🍀 util 包

📌 FileUtils
/**
 * Description: 文件相关工具
 *
 * @Author 狐狸半面添
 * @Create 2023/11/6 11:46
 * @Version 1.0
 */
public class FileUtils {
    /**
     * 获取本地文件的大小
     *
     * @param path 文件路径
     * @return 文件大小
     */
    public static long getFileContentLength(String path) {
        File file = new File(path);
        return file.exists() && file.isFile() ? file.length() : 0;
    }
}
📌 HttpUtils
/**
 * Description: Http 相关工具类
 *
 * @Author 狐狸半面添
 * @Create 2023/11/6 1:06
 * @Version 1.0
 */
public class HttpUtils {

    private static long id = System.currentTimeMillis();

    public static void change() {
        id = System.currentTimeMillis();
    }

    /**
     * 获取下载的文件大小
     *
     * @param url 下载文件链接
     * @return 文件大小
     * @throws IOException
     */
    public static long getHttpFileContentLength(String url) throws IOException {
        int contentLength;
        HttpURLConnection httpURLConnection = null;
        try {
            httpURLConnection = getHttpURLConnection(url);
            contentLength = httpURLConnection.getContentLength();
        } finally {
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }
        }
        return contentLength;
    }

    /**
     * 分块下载
     *
     * @param url      下载地址
     * @param startPos 下载文件起始位置
     * @param endPos   下载文件结束位置
     * @return 连接对象
     */
    public static HttpURLConnection getHttpURLConnection(String url, long startPos, long endPos) throws IOException {
        HttpURLConnection httpURLConnection = getHttpURLConnection(url);
        LogUtils.info("下载的区间是:{}-{}", startPos, endPos);

        if (endPos != 0) {
            httpURLConnection.setRequestProperty("RANGE", "bytes=" + startPos + "-" + endPos);
        } else {
            httpURLConnection.setRequestProperty("RANGE", "bytes=" + startPos + "-");
        }

        return httpURLConnection;
    }

    /**
     * 获取 HttpURLConnection 连接对象
     *
     * @param url 文件的地址
     * @return HttpURLConnection 连接对象
     */
    public static HttpURLConnection getHttpURLConnection(String url) throws IOException {
        URL httpUrl = new URL(url);
        HttpURLConnection httpURLConnection = (HttpURLConnection) httpUrl.openConnection();
        // 向文件所在的服务器发送标识信息
        httpURLConnection.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.1 (KHTML, like Gecko) Chrome/14.0.835.163 Safari/535.1");
        return httpURLConnection;
    }

    /**
     * 获取下载文件的名字
     *
     * @param url 下载地址
     * @return 文件名
     */
    public static String getHttpFileName(String url) {

        String fileName;

        int startIndex = url.lastIndexOf("/");
        int endIndex = url.lastIndexOf("?");
        if (endIndex == -1) {
            fileName = url.substring(startIndex + 1);
        } else {
            fileName = url.substring(startIndex + 1, endIndex);
        }

        int pointIndex = fileName.lastIndexOf(".");

        return fileName.substring(0, fileName.lastIndexOf(".")) + "-" + id + fileName.substring(pointIndex);
    }

}
📌 LogUtils
/**
 * Description: 日志工具类
 *
 * @Author 狐狸半面添
 * @Create 2023/11/6 1:41
 * @Version 1.0
 */
public class LogUtils {
    private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("hh:mm:ss");

    public static void info(String msg, Object... args) {
        print(msg, "-info-", args);
    }

    public static void error(String msg, Object... args) {
        print(msg, "-error-", args);
    }

    private static void print(String msg, String level, Object... args) {
        if (args != null && args.length > 0) {
            msg = String.format(msg.replace("{}", "%s"), args);
        }
        String threadName = Thread.currentThread().getName();
        System.out.println(LocalTime.now().format(FORMATTER) + "  " + threadName + level + msg);
    }
}

🍀 core 包

📌 DownloadInfoThread
/**
 * Description: 展示下载信息
 *
 * @Author 狐狸半面添
 * @Create 2023/11/6 2:07
 * @Version 1.0
 */
@SuppressWarnings("AlibabaUndefineMagicConstant")
public class DownloadInfoThread implements Runnable {
    /**
     * 下载文件总大小
     */
    private final long httpFileContentLength;


    /**
     * 本次累计下载的大小
     */
    public static volatile LongAdder downSize = new LongAdder();

    /**
     * 前一次下载的大小
     */
    public double prevSize;

    public DownloadInfoThread(long httpFileContentLength) {
        this.httpFileContentLength = httpFileContentLength;
    }

    @Override
    public void run() {
        // 计算文件总大小 单位是 MB
        String httpFileSize = String.format("%.2f", httpFileContentLength / Constant.MB);

        // 计算每秒下载速度 kb
        int speed = (int) ((downSize.doubleValue() - prevSize) / Constant.KB);

        prevSize = downSize.doubleValue();

        // 剩余文件的大小
        double remainSize = httpFileContentLength - downSize.doubleValue();

        // 计算剩余时间
        String remainTime = String.format("%.1f", remainSize / Constant.KB / speed);

        if ("Infinity".equalsIgnoreCase(remainTime)) {
            remainTime = "-";
        }

        // 已下载大小
        String currentFileSize = String.format("%.1f", downSize.doubleValue() / Constant.MB);

        String speedInfo = String.format("已下载 %smb/%smb,速度 %skb/s,剩余时间 %ss", currentFileSize, httpFileSize, speed, remainTime);

        System.out.print("\r");
        System.out.print(speedInfo);

    }
}
📌 DownloaderTask
/**
 * Description: 分块下载任务
 *
 * @Author 狐狸半面添
 * @Create 2023/11/7 0:58
 * @Version 1.0
 */
public class DownloaderTask implements Callable<Boolean> {

    private final String url;

    /**
     * 下载起始位置
     */
    private final long startPos;

    /**
     * 下载结束位置
     */
    private final long endPos;

    /**
     * 标识当前是哪一部分
     */
    private final int part;

    private final CountDownLatch countDownLatch;

    public DownloaderTask(String url, long startPos, long endPos, int part, CountDownLatch countDownLatch) {
        this.url = url;
        this.startPos = startPos;
        this.endPos = endPos;
        this.part = part;
        this.countDownLatch = countDownLatch;
    }

    @Override
    public Boolean call() throws Exception {
        // 获取文件名
        String httpFileName = HttpUtils.getHttpFileName(url);
        // 分块的文件名
        httpFileName = httpFileName + Constant.PART_FILE_SUFFIX + part;
        // 下载路径
        httpFileName = Constant.PATH + httpFileName;

        // 获取分块下载的连接
        HttpURLConnection httpURLConnection = HttpUtils.getHttpURLConnection(url, startPos, endPos);

        try (
                InputStream input = httpURLConnection.getInputStream();
                BufferedInputStream bis = new BufferedInputStream(input);
                RandomAccessFile accessFile = new RandomAccessFile(httpFileName, "rw");
        ) {
            byte[] buffer = new byte[Constant.BYTE_SIZE];
            int len;
            // 循环读取数据
            while ((len = bis.read(buffer)) != -1) {
                // 1s 内下载的数据,通过原子类下载
                DownloadInfoThread.downSize.add(len);
                accessFile.write(buffer, 0, len);
            }
        } catch (FileNotFoundException e) {
            LogUtils.error("下载文件不存在 {}", url);
            return false;
        } catch (Exception e) {
            LogUtils.error("下载出现异常");
            return false;
        } finally {
            httpURLConnection.disconnect();
            countDownLatch.countDown();
        }

        return true;
    }

}
📌 Downloader
/**
 * Description: 下载器
 *
 * @Author 狐狸半面添
 * @Create 2023/11/6 1:21
 * @Version 1.0
 */
public class Downloader {

    private final ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);

    public ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(Constant.THREAD_NUM,
            Constant.THREAD_NUM,
            0,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(5));

    private CountDownLatch countDownLatch = new CountDownLatch(Constant.THREAD_NUM);

    public void download(String url) {
        // 获取文件名
        String httpFileName = HttpUtils.getHttpFileName(url);
        // 文件下载路径
        httpFileName = Constant.PATH + httpFileName;
        // 获取本地文件的大小
        long localFileLength = FileUtils.getFileContentLength(httpFileName);


        HttpURLConnection httpURLConnection = null;
        DownloadInfoThread downloadInfoThread;
        try {
            // 获取连接对象
            httpURLConnection = HttpUtils.getHttpURLConnection(url);

            // 获取下载文件的总大小
            int contentLength = httpURLConnection.getContentLength();

            // 判断文件是否已下载过
            if (localFileLength >= contentLength) {
                LogUtils.info("{} 已下载完毕,无需重新下载", httpFileName);
                // 关闭连接对象
                httpURLConnection.disconnect();
                // 关闭线程池
                scheduledExecutorService.shutdownNow();
                poolExecutor.shutdown();

                return;
            }

            // 创建获取下载信息的任务对象
            downloadInfoThread = new DownloadInfoThread(contentLength);

            // 将任务交给线程执行,每隔 1s 打印一次
            scheduledExecutorService.scheduleAtFixedRate(downloadInfoThread, 1, 1, TimeUnit.SECONDS);

            // 切分任务
            ArrayList<Future> list = new ArrayList<>();
            split(url, list);

            countDownLatch.await();

            System.out.print("\r");
            System.out.println("分块文件下载完成");

            // 合并文件
            if (merge(httpFileName)) {
                // 清除临时文件
                clearTemp(httpFileName);
            }


        } catch (IOException | InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("本次执行完成");

            // 关闭连接对象
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }

            // 关闭线程池
            scheduledExecutorService.shutdownNow();
            poolExecutor.shutdown();
        }
    }

    /**
     * 文件切分
     *
     * @param url        文件链接
     * @param futureList 任务集合
     */
    public void split(String url, ArrayList<Future> futureList) {
        try {
            // 获取下载文件大小
            long contentLength = HttpUtils.getHttpFileContentLength(url);

            // 计算切分后的文件大小
            long size = contentLength / Constant.THREAD_NUM;

            // 计算分块个数
            for (int i = 0; i < Constant.THREAD_NUM; i++) {
                // 计算下载起始位置
                long startPos = i * size;

                // 计算结束位置
                long endPos;
                if (i == Constant.THREAD_NUM - 1) {
                    // 下载最后一块
                    endPos = 0;
                } else {
                    endPos = startPos + size - 1;
                }

                // 创建任务对象
                DownloaderTask downloaderTask = new DownloaderTask(url, startPos, endPos, i, countDownLatch);
                // 将任务提交到线程池
                Future<Boolean> future = poolExecutor.submit(downloaderTask);

                futureList.add(future);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 文件合并
     *
     * @param fileName 文件名
     * @return 是否合并成功
     */
    public boolean merge(String fileName) {
        LogUtils.info("开始合并文件 {}", fileName);
        byte[] buffer = new byte[Constant.BYTE_SIZE];
        int len;
        try (
                RandomAccessFile accessFile = new RandomAccessFile(fileName, "rw")
        ) {
            for (int i = 0; i < Constant.THREAD_NUM; i++) {
                try (
                        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName + Constant.PART_FILE_SUFFIX + i))
                ) {
                    while ((len = bis.read(buffer)) != -1) {
                        accessFile.write(buffer, 0, len);
                    }

                }
            }

            LogUtils.info("文件合并完毕 {}", fileName);

        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }

        return true;
    }

    /**
     * 清除临时文件
     *
     * @param fileName 文件名
     */
    public void clearTemp(String fileName) {
        LogUtils.info("清理分块文件");
        for (int i = 0; i < Constant.THREAD_NUM; i++) {
            String name = fileName + Constant.PART_FILE_SUFFIX + i;
            File file = new File(name);
            file.delete();
        }
        LogUtils.info("分块清除完毕");
    }
}

🍀 Main 主类

public class Main {
    public static void main(String[] args) {
        // 下载地址
        String url = null;

        if (args == null || args.length == 0) {
            while (url == null || url.trim().isEmpty()) {
                System.out.print("请输入下载链接:");
                Scanner scanner = new Scanner(System.in);
                url = scanner.next();
            }
        } else {
            url = args[0];
        }

        Downloader downloader = new Downloader();
        downloader.download(url);

    }
}

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

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

相关文章

多维时序 | MATLAB实现SOM-BP自组织映射结合BP神经网络的多变量时间序列预测

多维时序 | MATLAB实现SOM-BP自组织映射结合BP神经网络的多变量时间序列预测 目录 多维时序 | MATLAB实现SOM-BP自组织映射结合BP神经网络的多变量时间序列预测预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 MATLAB实现SOM-BP自组织映射结合BP神经网络的多变量时…

Qt 自定义分页控件

目录 前言1、功能描述2、代码实现2.1 ui文件2.1 头文件2.2 源码文件2.3 设计思路 4、示例5、总结 前言 在应用程序开发时经常会遇到数据分页的需求&#xff0c;每一页展示特定数量的数据&#xff0c;通过点击按钮翻页或者输入页码跳转到指定页。 本文介绍一个自定义分页控件&a…

微服务-开篇-个人对微服务的理解

从吃饭说起 个人理解新事物的时候喜欢将天上飞的理念转换成平常生活中的实践&#xff0c;对比理解这些高大上的名词&#xff0c;才能让我们减少恐慌的同时加深理解。废话不多说&#xff0c;我们从吃饭开始说起&#xff0c;逐渐类比出微服务的思想。 &#xff08;个人见解&…

计蒜客详解合集(1)期

以后对于简单题目&#xff0c;大致6道题目出一期&#xff0c;稍有难度的才单独一期发布~ 目录 T1266——出勤记录 T1170——人民币支付 T1122——最长最短单词 T1115——字符串判等 T1116——验证子串 T1118——删除单词后缀 T1266——出勤记录 小蒜的算法课老师每次…

任正非说:段到段而不是端到端的变革,一定会局部优秀了,而全局灾难了。

你好&#xff01;这是华研荟【任正非说】系列的第34篇文章&#xff0c;让我们聆听任正非先生的真知灼见&#xff0c;学习华为的管理思想和管理理念。 一、执行一个良好的流程和建立一个良好的流程同样重要。业务部门的一把手要担负起建设和优化流程的责任&#xff0c;而不是流程…

NFS服务器的搭建

架设一台NFS服务器&#xff0c;并按照以下要求配置 准备阶段&#xff1a;准备两台虚拟机&#xff0c;一台作为服务端&#xff0c;一台作为客户端 服务端&#xff08;Server&#xff09;&#xff1a;192.168.75.139 客户端&#xff08;Client&#xff09;:192.168.75.160 两…

【Java 进阶篇】Java Filter 执行流程及生命周期详解

引言 在 Java Web 开发中&#xff0c;Filter 是一种强大的组件&#xff0c;它允许我们在请求到达 Servlet 之前或者响应返回给客户端之前执行一些操作。Filter 的应用场景非常广泛&#xff0c;例如日志记录、权限验证、字符编码转换等。本文将深入讨论 Java Filter 的执行流程…

element-Cascader级联选择器用法?

html <el-form-item label"行业选择" :label-width"formLabelWidth"><div class"m-4"><el-cascader v-model"form.tradeid" :options"options" :props"props" /></div></el-form-ite…

【仿真动画】人机协作机器人自动化产线仿真动画欣赏

人机协作机器人自动化产线仿真动画 动画部分思维导图 机器人自动化产线仿真动画是利用三维动画技术对机器人自动化产线进行仿真模拟&#xff0c;以直观、形象的方式展示产线的运行情况。它具有以下作用&#xff1a; 提高沟通效率 机器人自动化产线的设计、实施和维护涉及多个部…

19.9 Boost Asio 同步字典传输

这里所代指的字典是Python中的样子&#xff0c;本节内容我们将通过使用Boost中自带的Tokenizer分词器实现对特定字符串的切割功能&#xff0c;使用Boost Tokenizer&#xff0c;可以通过构建一个分隔符或正则表达式的实例来初始化tokenizer。然后&#xff0c;可以使用该实例对输…

阿里云服务器u1和e实例有什么区别?哪个比较好?

阿里云服务器u1和e实例有什么区别&#xff1f;哪个比较好&#xff1f;通用算力型u1比较好&#xff0c;因为u1服务器是独享型云服务器&#xff0c;e实例是共享型。 阿里云服务器ECS经济型e实例和通用算力型u1实例有什么区别&#xff1f;如何选择&#xff1f;ECS经济型e实例是共…

Modern C++ 转换构造函数和类型转换函数

在 C/C 中&#xff0c;不同的数据类型之间可以相互转换。无需用户指明如何转换的称为自动类型转换&#xff08;隐式类型转换&#xff09;&#xff0c;需要用户显式地指明如何转换的称为强制类型转换。 不管是自动类型转换还是强制类型转换&#xff0c;前提必须是编译器知道如何…

U盘插在电脑上显示要格式化磁盘怎么办

U盘是一种便携式存储设备&#xff0c;广泛应用于各种场合。然而&#xff0c;有时候我们可能会遇到一些问题&#xff0c;比如将U盘插入电脑后显示要格式化磁盘&#xff0c;这通常意味着U盘的分区出现了问题或者U盘的文件系统已经损坏。这种情况下&#xff0c;我们应该如何解决呢…

linux 显卡驱动 cuda 离线安装

1、 安装显卡驱动&#xff1a; Download NVIDIA, GeForce, Quadro, and Tesla Drivers &#xff08;1&#xff09;注意选择对应的cuda版本&#xff0c;和系统版本&#xff0c;并下载 &#xff08;2&#xff09;

client-go controller-runtime kubebuilder

背景 这半年一直做k8s相关的工作&#xff0c;一直接触client-go controller-runtime kubebuilder&#xff0c;但是很少有文章将这三个的区别说明白&#xff0c;直接用框架是简单&#xff0c;但是出了问题就是黑盒&#xff0c;这不符合我的理念&#xff0c;所以这篇文章从头说起…

【C语言 | 预处理】C语言预处理详解(一) —— #define、#under、#if、#else、#elif、#endif、#include、#error

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…

Python基础教程之十八:Python字典交集–比较两个字典

Python示例&#xff0c;用于查找2个或更多词典之间的常见项目&#xff0c;即字典相交项目。 1.使用“&#xff06;”运算符的字典交集 最简单的方法是查找键&#xff0c;值或项的交集&#xff0c;即 & 在两个字典之间使用运算符。 example.pya { x : 1, y : 2, z : 3 }…

OCR技术狂潮:揭秘最新发展现状,引爆未来智能时代

OCR&#xff08;Optical Character Recognition&#xff0c;光学字符识别&#xff09;技术自20世纪以来经历了长足的发展&#xff0c;随着计算机视觉、人工智能和深度学习等领域的进步&#xff0c;OCR技术在准确性、速度和适用范围上都取得了显著的进展。以下是OCR技术发展的现…

Verilog 之 initial 模块与always 模块的用法与差异

文章目录 initial语法和用法特点和注意事项用途 always语法和用法特点和注意事项用途 二者差异 initial 在 Verilog 中&#xff0c;initial 块是用来在模拟开始时执行一次性初始化操作的一种建模方式。它通常用于模拟初始条件或进行一次性的初始化设置&#xff0c;而且只会在模…

深度学习(生成式模型)——Classifier Free Guidance Diffusion

文章目录 前言推导流程训练流程测试流程 前言 在上一节中&#xff0c;我们总结了Classifier Guidance Diffusion&#xff0c;其有两个弊端&#xff0c;一是需要额外训练一个分类头&#xff0c;引入了额外的训练开销。二是要噪声图像通常难以分类&#xff0c;分类头通常难以学习…